@craftrpgs/cli 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -10
  2. package/dist/bin.js +348 -178
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -44,7 +44,6 @@ know how to work with the project the moment you open it.
44
44
  | `craft type new <Name>` | Author a new file type locally |
45
45
  | `craft image generate/upload` | Generate (including local or workspace reference images; opt-in in Settings) or upload images |
46
46
  | `craft image expressions/cutouts` | Preflight and persist character expression or cutout assets |
47
- | `craft env [name]` | Show or pin the environment for a directory |
48
47
 
49
48
  Every command takes `--json` for machine-readable output. Auth is via
50
49
  `craft login` (browser OAuth); credentials are stored locally and refreshed
@@ -52,16 +51,14 @@ automatically.
52
51
 
53
52
  Run `craft help <command>` for details on any of them.
54
53
 
55
- ## Environments
54
+ ## Staying up to date
56
55
 
57
- The CLI talks to production (`craftrpgs.com`) by default. `craft env <name-or-url>`
58
- pins the current directory tree to another server via a one-line
59
- `.craft-env` file — handy for keeping a testing folder pointed at a dev
60
- server while the rest of your machine uses production. `--remote` and
61
- `CRAFT_REMOTE` accept an environment name or any origin URL. A cloned
62
- workspace always syncs with the server it was cloned from. (Craft
63
- developers: set `CRAFT_DEV=1` to enable the internal `local` and `staging`
64
- environment names.)
56
+ The server only accepts the latest published version of the CLI. When a
57
+ command is refused with a `cli_outdated` error, update and retry:
58
+
59
+ ```bash
60
+ npm install -g @craftrpgs/cli@latest
61
+ ```
65
62
 
66
63
  ## Requirements
67
64
 
package/dist/bin.js CHANGED
@@ -26773,6 +26773,7 @@ function truncateImageStyleInstructions(instructions) {
26773
26773
  function normalizeImageGenerationPrompt(prompt) {
26774
26774
  return truncateImageGenerationPrompt(prompt.trim());
26775
26775
  }
26776
+ var NON_DETAIL_KEYS = new Set(["name", "title", "description", "content"]);
26776
26777
 
26777
26778
  // ../shared/src/context-visibility.ts
26778
26779
  var CONTEXT_VISIBILITY_VALUES = [
@@ -26839,8 +26840,18 @@ function isAllowedImageUrl(value) {
26839
26840
  return false;
26840
26841
  }
26841
26842
  }
26843
+ var CHAT_ATTACHMENT_PATH_PREFIX = "/attachments/";
26844
+ function isChatAttachmentUrl(value) {
26845
+ try {
26846
+ return new URL(value).pathname.startsWith(CHAT_ATTACHMENT_PATH_PREFIX);
26847
+ } catch {
26848
+ return false;
26849
+ }
26850
+ }
26842
26851
  var imageUrlSchema = exports_external.string().trim().min(1).max(2048).url().refine(isAllowedImageUrl, {
26843
26852
  message: "Image URL must use an approved media host."
26853
+ }).refine((value) => !isChatAttachmentUrl(value), {
26854
+ message: "Chat attachments can't be saved as images. Upload the image instead."
26844
26855
  });
26845
26856
 
26846
26857
  // ../shared/src/validators/image-object.ts
@@ -26939,6 +26950,63 @@ var imageObjectSchema = exports_external.preprocess((input) => normalizeImageInp
26939
26950
  variations: exports_external.array(imageVariationSchema).max(MAX_FILE_TYPE_STATES).optional()
26940
26951
  }).strict());
26941
26952
 
26953
+ // ../shared/src/projects/first-steps.ts
26954
+ var FIRST_STEPS_VERSION = 1;
26955
+ var MAX_FIRST_STEPS_STEPS = 8;
26956
+ var MAX_FIRST_STEPS_WELCOME_LENGTH = 500;
26957
+ var MAX_FIRST_STEPS_STEP_TITLE_LENGTH = 80;
26958
+ var MAX_FIRST_STEPS_STEP_BODY_LENGTH = 300;
26959
+ var firstStepsStepIdSchema = exports_external.string().min(1).max(64);
26960
+ var firstStepsStepTitleSchema = exports_external.string().trim().min(1).max(MAX_FIRST_STEPS_STEP_TITLE_LENGTH);
26961
+ var firstStepsStepBodySchema = exports_external.string().trim().max(MAX_FIRST_STEPS_STEP_BODY_LENGTH);
26962
+ var firstStepsActionSchema = exports_external.discriminatedUnion("type", [
26963
+ exports_external.object({
26964
+ type: exports_external.literal("create-file"),
26965
+ fileTypeSlug: exports_external.string().trim().min(1).max(120),
26966
+ setAsRootMap: exports_external.boolean().optional()
26967
+ }),
26968
+ exports_external.object({
26969
+ type: exports_external.literal("open-folder"),
26970
+ folderPath: exports_external.string().trim().min(1).max(512)
26971
+ }),
26972
+ exports_external.object({
26973
+ type: exports_external.literal("open-file"),
26974
+ folderPath: exports_external.string().trim().max(512),
26975
+ fileName: exports_external.string().trim().min(1).max(255)
26976
+ }),
26977
+ exports_external.object({
26978
+ type: exports_external.literal("read-handbook"),
26979
+ book: exports_external.enum(["player", "builder"]),
26980
+ chapterId: exports_external.string().min(1).max(64).nullable().optional()
26981
+ }),
26982
+ exports_external.object({ type: exports_external.literal("open-assistant") }),
26983
+ exports_external.object({ type: exports_external.literal("set-cover-image") }),
26984
+ exports_external.object({ type: exports_external.literal("edit-overview") })
26985
+ ]);
26986
+ var firstStepsStepSchema = exports_external.discriminatedUnion("kind", [
26987
+ exports_external.object({
26988
+ kind: exports_external.literal("action"),
26989
+ id: firstStepsStepIdSchema,
26990
+ title: firstStepsStepTitleSchema,
26991
+ body: firstStepsStepBodySchema.optional(),
26992
+ action: firstStepsActionSchema
26993
+ }),
26994
+ exports_external.object({
26995
+ kind: exports_external.literal("info"),
26996
+ id: firstStepsStepIdSchema,
26997
+ title: firstStepsStepTitleSchema,
26998
+ body: firstStepsStepBodySchema.optional()
26999
+ })
27000
+ ]);
27001
+ var firstStepsSchema = exports_external.object({
27002
+ version: exports_external.literal(FIRST_STEPS_VERSION),
27003
+ updatedAt: exports_external.string().max(40).optional(),
27004
+ image: imageObjectSchema.nullable().optional(),
27005
+ welcome: exports_external.string().trim().max(MAX_FIRST_STEPS_WELCOME_LENGTH).optional(),
27006
+ steps: exports_external.array(firstStepsStepSchema).max(MAX_FIRST_STEPS_STEPS)
27007
+ });
27008
+ var RETIRED_ACTION_TYPES = new Set(["start-game"]);
27009
+
26942
27010
  // ../shared/src/projects/manual.ts
26943
27011
  var MANUAL_VERSION = 1;
26944
27012
  var MAX_MANUAL_CHAPTERS = 50;
@@ -27045,7 +27113,8 @@ var projectImportProjectSettingsSchema = exports_external.object({
27045
27113
  gm: projectImportGameMasterSettingsSchema.optional(),
27046
27114
  preludeFlow: exports_external.unknown().nullable().optional(),
27047
27115
  playerHandbook: manualSchema.nullable().optional(),
27048
- builderManual: manualSchema.nullable().optional()
27116
+ builderManual: manualSchema.nullable().optional(),
27117
+ firstSteps: firstStepsSchema.nullable().optional()
27049
27118
  }).strict();
27050
27119
  var projectImportProjectSchema = exports_external.object({
27051
27120
  name: exports_external.string().trim().min(1),
@@ -27059,17 +27128,12 @@ var projectImportFileTypeDesignationSchema = exports_external.enum([
27059
27128
  "gm_instructions",
27060
27129
  "game_start"
27061
27130
  ]);
27062
- var projectImportFileTypeCategorySchema = exports_external.enum([
27063
- "world",
27064
- "game_master",
27065
- "game_system"
27066
- ]);
27067
27131
  var projectImportFileTypeSchema = exports_external.object({
27068
27132
  key: projectImportKeySchema,
27069
27133
  name: exports_external.string().trim().min(1),
27070
27134
  contentType: exports_external.enum(["markdown", "json"]),
27071
27135
  designation: projectImportFileTypeDesignationSchema.nullable().optional(),
27072
- category: projectImportFileTypeCategorySchema.nullable().optional(),
27136
+ category: exports_external.unknown().optional(),
27073
27137
  contextVisibility: exports_external.enum(CONTEXT_VISIBILITY_VALUES).optional(),
27074
27138
  agentEditable: exports_external.boolean().optional(),
27075
27139
  schema: projectImportJsonValueSchema.nullable().optional(),
@@ -27189,7 +27253,7 @@ var workspaceFileTypeFileSchema = exports_external.object({
27189
27253
  name: exports_external.string().min(1),
27190
27254
  contentType: exports_external.enum(["markdown", "json"]),
27191
27255
  designation: projectImportFileTypeDesignationSchema.nullable(),
27192
- category: exports_external.string().min(1).nullable().optional(),
27256
+ category: exports_external.unknown().optional(),
27193
27257
  schemaHash: schemaHashSchema.optional(),
27194
27258
  schema: projectImportJsonValueSchema.nullable(),
27195
27259
  layout: projectImportJsonValueSchema.nullable(),
@@ -27307,7 +27371,7 @@ var cliPushCreateFileTypeOpSchema = exports_external.object({
27307
27371
  contentType: exports_external.enum(["markdown", "json"]),
27308
27372
  schema: projectImportJsonValueSchema.nullable().optional(),
27309
27373
  layout: projectImportJsonValueSchema.nullable().optional(),
27310
- category: exports_external.string().min(1).nullable().optional(),
27374
+ category: exports_external.unknown().optional(),
27311
27375
  designation: projectImportFileTypeDesignationSchema.nullable().optional(),
27312
27376
  imageStyleInstructions: exports_external.string().transform(truncateImageStyleInstructions).nullable().optional(),
27313
27377
  showCoverImage: exports_external.boolean().optional(),
@@ -27740,6 +27804,12 @@ var cliFileRemoteStateSchema = exports_external.enum([
27740
27804
  "moved"
27741
27805
  ]);
27742
27806
 
27807
+ // ../shared/src/uuid.ts
27808
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
27809
+ function isUuid(value) {
27810
+ return UUID_REGEX.test(value);
27811
+ }
27812
+
27743
27813
  // ../shared/src/expression/values.ts
27744
27814
  class ExpressionLambda {
27745
27815
  invoke;
@@ -28010,14 +28080,13 @@ var GLOBAL_BUILTINS_TABLE = {
28010
28080
  }
28011
28081
  }
28012
28082
  };
28013
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
28014
28083
  function referenceIdFromValue(value) {
28015
28084
  if (typeof value === "string") {
28016
- return UUID_RE.test(value) ? value : null;
28085
+ return isUuid(value) ? value : null;
28017
28086
  }
28018
28087
  if (isPlainObjectValue(value)) {
28019
28088
  const id = value.referenceId;
28020
- return typeof id === "string" && UUID_RE.test(id) ? id : null;
28089
+ return typeof id === "string" && isUuid(id) ? id : null;
28021
28090
  }
28022
28091
  return null;
28023
28092
  }
@@ -35455,7 +35524,6 @@ var LOCKED_DESIGNATION_CONFIGS = {
35455
35524
  game_start: {
35456
35525
  name: "Game Start",
35457
35526
  contentType: "json",
35458
- category: "game_master",
35459
35527
  contextVisibility: "searchable",
35460
35528
  schemaMode: "base_fields_only",
35461
35529
  layoutMode: "none"
@@ -36009,7 +36077,6 @@ var extraTargetCandidatesCache = new WeakMap;
36009
36077
 
36010
36078
  // ../shared/src/cli/craft-block.ts
36011
36079
  var CRAFT_BLOCK_KEY = "$craft";
36012
- var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
36013
36080
  var KNOWN_BLOCK_KEYS = new Set(["referenceId", "settings", "metadata"]);
36014
36081
  var KNOWN_SETTINGS_KEYS = new Set([
36015
36082
  "contextVisibility",
@@ -36044,7 +36111,7 @@ function parseCraftBlockValue(value) {
36044
36111
  }
36045
36112
  }
36046
36113
  if (value.referenceId !== undefined) {
36047
- if (typeof value.referenceId === "string" && UUID_REGEX.test(value.referenceId)) {
36114
+ if (typeof value.referenceId === "string" && isUuid(value.referenceId)) {
36048
36115
  block.referenceId = value.referenceId.toLowerCase();
36049
36116
  } else {
36050
36117
  warnings.push("$craft.referenceId is not a UUID — treated as absent (the file imports as id-less)");
@@ -37887,8 +37954,22 @@ var {
37887
37954
  } = shadcnComponentDefinitions;
37888
37955
  var roundedEnum = exports_external.nullable(exports_external.enum(["none", "sm", "md", "lg", "xl", "2xl", "full"]));
37889
37956
  var aspectRatioEnum = exports_external.nullable(exports_external.enum(["video", "square", "portrait", "tall", "ultrawide", "ultratall"]));
37957
+ var bgImageProp = exports_external.optional(exports_external.nullable(exports_external.string()));
37958
+ var bgImageFitEnum = exports_external.optional(exports_external.nullable(exports_external.enum(["cover", "contain", "tile", "tile-x", "tile-y"])));
37959
+ var BG_IMAGE_DOC = 'bgImage sets a background image URL (children render on top); bgImageFit controls how it fills: "cover" (default, crop to fill), "contain" (fit inside), "tile" / "tile-x" / "tile-y" (repeat at natural size for seamless textures).';
37890
37960
  var editPathProp = exports_external.optional(exports_external.nullable(exports_external.record(exports_external.string(), exports_external.string())));
37891
37961
  var EDIT_PATH_DOC = 'editPath redirects where an editable prop saves — e.g. editPath: { "value": "/resourceState/{key}/current" } writes there instead of the bound path, with {placeholders} filled from the current repeat item; this is how a control repeated over a computed (expression) array stays editable: display the computed row, write to the stored field.';
37962
+ var classNameProp = exports_external.optional(exports_external.nullable(exports_external.string()));
37963
+ function withClassNameProp(definition) {
37964
+ const props = definition.props;
37965
+ if (typeof props?.extend !== "function") {
37966
+ return definition;
37967
+ }
37968
+ return {
37969
+ ...definition,
37970
+ props: props.extend({ className: classNameProp })
37971
+ };
37972
+ }
37892
37973
  var extendedShadcnComponents = {
37893
37974
  Image: {
37894
37975
  props: exports_external.object({
@@ -37897,7 +37978,8 @@ var extendedShadcnComponents = {
37897
37978
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg", "full"])),
37898
37979
  align: exports_external.nullable(exports_external.enum(["left", "center", "right"])),
37899
37980
  aspectRatio: exports_external.optional(aspectRatioEnum),
37900
- stateKey: exports_external.optional(exports_external.nullable(exports_external.string()))
37981
+ stateKey: exports_external.optional(exports_external.nullable(exports_external.string())),
37982
+ className: classNameProp
37901
37983
  }),
37902
37984
  description: 'Image display with configurable size and alignment. size controls max width: sm (200px), md (400px), lg (600px), full (100%). align controls horizontal alignment: left, center, right. Defaults to full size, left aligned. aspectRatio crops the image to a fixed ratio: "video" (16:9), "square" (1:1), "portrait" (3:4), "tall" (9:16), "ultrawide" (32:9), "ultratall" (9:32); omit to keep the natural ratio.',
37903
37985
  example: {
@@ -37915,11 +37997,13 @@ var extendedShadcnComponents = {
37915
37997
  justify: exports_external.nullable(exports_external.enum(["start", "center", "end", "between", "around"])),
37916
37998
  wrap: exports_external.nullable(exports_external.boolean()),
37917
37999
  bgColor: exports_external.nullable(exports_external.string()),
38000
+ bgImage: bgImageProp,
38001
+ bgImageFit: bgImageFitEnum,
37918
38002
  rounded: roundedEnum,
37919
38003
  className: exports_external.nullable(exports_external.string())
37920
38004
  }),
37921
38005
  slots: shadcnComponentDefinitions.Stack.slots,
37922
- description: "Flex layout container. bgColor sets background color (any CSS color value). Use className for extra Tailwind utilities (e.g. col-span-*, mt-4). Set wrap=true to allow line wrapping.",
38006
+ description: `Flex layout container. bgColor sets background color (any CSS color value). ${BG_IMAGE_DOC} Use className for extra Tailwind utilities (e.g. col-span-*, mt-4). Set wrap=true to allow line wrapping.`,
37923
38007
  example: { direction: "vertical", gap: "md" }
37924
38008
  },
37925
38009
  Grid: {
@@ -37927,11 +38011,13 @@ var extendedShadcnComponents = {
37927
38011
  columns: exports_external.nullable(exports_external.number()),
37928
38012
  gap: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
37929
38013
  bgColor: exports_external.nullable(exports_external.string()),
38014
+ bgImage: bgImageProp,
38015
+ bgImageFit: bgImageFitEnum,
37930
38016
  rounded: roundedEnum,
37931
38017
  className: exports_external.nullable(exports_external.string())
37932
38018
  }),
37933
38019
  slots: shadcnComponentDefinitions.Grid.slots,
37934
- description: "CSS grid layout. columns sets equal column count (supports 1-12). bgColor sets background color (any CSS color value). Use className for extra Tailwind utilities. Children can use className with col-span-* for responsive multi-column layouts.",
38020
+ description: `CSS grid layout. columns sets equal column count (supports 1-12). bgColor sets background color (any CSS color value). ${BG_IMAGE_DOC} Use className for extra Tailwind utilities. Children can use className with col-span-* for responsive multi-column layouts.`,
37935
38021
  example: { columns: 2, gap: "md" }
37936
38022
  },
37937
38023
  Card: {
@@ -37941,12 +38027,14 @@ var extendedShadcnComponents = {
37941
38027
  maxWidth: exports_external.nullable(exports_external.enum(["sm", "md", "lg", "full"])),
37942
38028
  centered: exports_external.nullable(exports_external.boolean()),
37943
38029
  bgColor: exports_external.nullable(exports_external.string()),
38030
+ bgImage: bgImageProp,
38031
+ bgImageFit: bgImageFitEnum,
37944
38032
  borderColor: exports_external.nullable(exports_external.string()),
37945
38033
  rounded: roundedEnum,
37946
38034
  className: exports_external.nullable(exports_external.string())
37947
38035
  }),
37948
38036
  slots: shadcnComponentDefinitions.Card.slots,
37949
- description: "Container card with optional title/description. bgColor and borderColor accept any CSS color value. Use className for extra Tailwind utilities (e.g. col-span-*).",
38037
+ description: `Container card with optional title/description. bgColor and borderColor accept any CSS color value. ${BG_IMAGE_DOC} Use className for extra Tailwind utilities (e.g. col-span-*).`,
37950
38038
  example: {
37951
38039
  title: "Character Stats",
37952
38040
  description: null
@@ -37956,7 +38044,8 @@ var extendedShadcnComponents = {
37956
38044
  props: exports_external.object({
37957
38045
  text: exports_external.string(),
37958
38046
  level: exports_external.nullable(exports_external.enum(["h1", "h2", "h3", "h4"])),
37959
- color: exports_external.nullable(exports_external.string())
38047
+ color: exports_external.nullable(exports_external.string()),
38048
+ className: classNameProp
37960
38049
  }),
37961
38050
  description: "Section heading (h1–h4). Use color for custom text color (any CSS color value). Defaults to h2 if level is omitted.",
37962
38051
  example: { text: "Character Stats", level: "h3", color: "#ef4444" }
@@ -37966,6 +38055,7 @@ var extendedShadcnComponents = {
37966
38055
  text: exports_external.string(),
37967
38056
  variant: exports_external.nullable(exports_external.enum(["caption", "body", "muted", "lead", "code"])),
37968
38057
  color: exports_external.nullable(exports_external.string()),
38058
+ className: classNameProp,
37969
38059
  editPath: editPathProp
37970
38060
  }),
37971
38061
  description: `Text paragraph with optional variant and custom color. Accepts any CSS color value. Bound text edits inline. ${EDIT_PATH_DOC}`,
@@ -37975,7 +38065,8 @@ var extendedShadcnComponents = {
37975
38065
  props: exports_external.object({
37976
38066
  text: exports_external.string(),
37977
38067
  variant: exports_external.nullable(exports_external.enum(["default", "secondary", "destructive", "outline"])),
37978
- color: exports_external.nullable(exports_external.string())
38068
+ color: exports_external.nullable(exports_external.string()),
38069
+ className: classNameProp
37979
38070
  }),
37980
38071
  description: "Small inline badge/tag. When color is set (any CSS color value), it overrides the variant with a custom background color and auto-contrasted text.",
37981
38072
  example: { text: "Active", variant: "default", color: null }
@@ -37989,6 +38080,7 @@ var extendedShadcnComponents = {
37989
38080
  trackColor: exports_external.nullable(exports_external.string()),
37990
38081
  showValue: exports_external.nullable(exports_external.boolean()),
37991
38082
  stepper: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38083
+ className: classNameProp,
37992
38084
  editPath: editPathProp
37993
38085
  }),
37994
38086
  description: `Progress bar with optional label. color sets the indicator bar color, trackColor sets the background track color. showValue displays "value / max" text. stepper adds hover −/+ buttons that bump the bound value by 1 (clamped to 0..max) — prefer this over pairing a bar with a separate Stepper for the same field. All colors accept any CSS color value. ${EDIT_PATH_DOC}`,
@@ -38006,7 +38098,8 @@ var extendedShadcnComponents = {
38006
38098
  tabs: exports_external.array(exports_external.object({ label: exports_external.string(), value: exports_external.string() })),
38007
38099
  defaultValue: exports_external.nullable(exports_external.string()),
38008
38100
  value: exports_external.nullable(exports_external.string()),
38009
- bgColor: exports_external.nullable(exports_external.string())
38101
+ bgColor: exports_external.nullable(exports_external.string()),
38102
+ className: classNameProp
38010
38103
  }),
38011
38104
  slots: _ShadcnTabs.slots,
38012
38105
  description: "Tab navigation. bgColor sets the tab list background color (any CSS color value).",
@@ -38023,6 +38116,10 @@ var referenceTargetSlugProps = {
38023
38116
  fileTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string())),
38024
38117
  pageTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string()))
38025
38118
  };
38119
+ var multiReferenceTargetSlugProps = {
38120
+ ...referenceTargetSlugProps,
38121
+ fileTypeSlugs: exports_external.optional(exports_external.nullable(exports_external.array(exports_external.string())))
38122
+ };
38026
38123
  var referenceValue = exports_external.union([
38027
38124
  exports_external.string(),
38028
38125
  exports_external.number(),
@@ -38035,7 +38132,7 @@ var referenceValue = exports_external.union([
38035
38132
  ]);
38036
38133
  var fileReferenceGridProps = exports_external.object({
38037
38134
  items: exports_external.array(exports_external.unknown()),
38038
- ...referenceTargetSlugProps,
38135
+ ...multiReferenceTargetSlugProps,
38039
38136
  referenceField: exports_external.nullable(exports_external.string()),
38040
38137
  columns: exports_external.nullable(exports_external.number()),
38041
38138
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
@@ -38045,18 +38142,24 @@ var fileReferenceGridProps = exports_external.object({
38045
38142
  showAddNewOnHover: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38046
38143
  addNewVariant: exports_external.optional(exports_external.nullable(exports_external.enum(["inline", "aside"]))),
38047
38144
  emptyState: exports_external.optional(exports_external.nullable(exports_external.string())),
38145
+ titlePosition: exports_external.optional(exports_external.nullable(exports_external.enum(["bottom", "top", "overlay", "hidden"]))),
38146
+ metadataPosition: exports_external.optional(exports_external.nullable(exports_external.enum(["inline", "below", "hidden"]))),
38048
38147
  metadataFields: exports_external.optional(exports_external.nullable(exports_external.array(exports_external.object({
38049
38148
  name: exports_external.string(),
38050
38149
  type: exports_external.enum(["string", "number", "boolean"]),
38051
- enum: exports_external.optional(exports_external.array(exports_external.string()))
38052
- }))))
38150
+ enum: exports_external.optional(exports_external.array(exports_external.string())),
38151
+ hidden: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38152
+ label: exports_external.optional(exports_external.nullable(exports_external.string()))
38153
+ })))),
38154
+ className: classNameProp
38053
38155
  });
38054
38156
  var craftComponents = {
38055
38157
  Checkbox: {
38056
38158
  props: exports_external.object({
38057
38159
  checked: exports_external.boolean(),
38058
38160
  label: exports_external.nullable(exports_external.string()),
38059
- color: exports_external.nullable(exports_external.string())
38161
+ color: exports_external.nullable(exports_external.string()),
38162
+ className: classNameProp
38060
38163
  }),
38061
38164
  description: "Labeled checkbox for a boolean field. Bind checked to toggle it directly from the rendered view. color sets the checked box color (any CSS color value).",
38062
38165
  example: { checked: true, label: "Inspiration", color: null }
@@ -38065,7 +38168,8 @@ var craftComponents = {
38065
38168
  props: exports_external.object({
38066
38169
  checked: exports_external.boolean(),
38067
38170
  label: exports_external.nullable(exports_external.string()),
38068
- color: exports_external.nullable(exports_external.string())
38171
+ color: exports_external.nullable(exports_external.string()),
38172
+ className: classNameProp
38069
38173
  }),
38070
38174
  description: "Labeled toggle switch for a boolean field — same data shape as Checkbox with a switch look. Bind checked to flip it directly from the rendered view. color sets the on-state color (any CSS color value).",
38071
38175
  example: { checked: true, label: "Concentrating", color: "#34d399" }
@@ -38080,7 +38184,8 @@ var craftComponents = {
38080
38184
  color: exports_external.nullable(exports_external.string()),
38081
38185
  emptyText: exports_external.nullable(exports_external.string()),
38082
38186
  showAddNew: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38083
- showAddNewOnHover: exports_external.optional(exports_external.nullable(exports_external.boolean()))
38187
+ showAddNewOnHover: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38188
+ className: classNameProp
38084
38189
  }),
38085
38190
  description: 'Plain list of values — traits, features, languages, rumors. items can be strings or objects (map object fields with labelField and descriptionField). variant "bullet" (default) and "numbered" stack items; "inline" flows them on one line joined by separator (default " · "). Bound string lists are editable inline. showAddNew defaults to true; set it false to hide the add control. showAddNewOnHover defaults to true; set it false to keep Add visible on desktop. color sets the item text color.',
38086
38191
  example: {
@@ -38103,7 +38208,8 @@ var craftComponents = {
38103
38208
  icon: exports_external.nullable(exports_external.string()),
38104
38209
  variant: exports_external.nullable(exports_external.enum(["quote", "panel"])),
38105
38210
  color: exports_external.nullable(exports_external.string()),
38106
- bgColor: exports_external.nullable(exports_external.string())
38211
+ bgColor: exports_external.nullable(exports_external.string()),
38212
+ className: classNameProp
38107
38213
  }),
38108
38214
  description: 'Framed lore/flavor block. variant "quote" (default) renders italic text behind a left accent border with an optional em-dash attribution; "panel" renders a soft filled box with an optional icon and title. icon accepts any text/emoji (e.g. "\uD83D\uDCDC"). color tints the accent and title, bgColor sets the panel background.',
38109
38215
  example: {
@@ -38126,7 +38232,8 @@ var craftComponents = {
38126
38232
  color: exports_external.nullable(exports_external.string()),
38127
38233
  fileTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string())),
38128
38234
  fileTypeSlugs: exports_external.optional(exports_external.nullable(exports_external.array(exports_external.string()))),
38129
- pageTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string()))
38235
+ pageTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string())),
38236
+ className: classNameProp
38130
38237
  }),
38131
38238
  description: `Live value pulled from a referenced file — display another file type's data without copying it. Bind source to a reference field (single or list) and set field to the path to read from each referenced file's content (e.g. "acBonus", "stats.speed"); "$title"/"$name" read the file name. Multiple referenced files join with separator (default ", ") or render as chips with format "badge". Read-only — the value lives on the referenced file. Provide fileTypeSlug when the schema does not already annotate the reference field.`,
38132
38239
  example: {
@@ -38142,7 +38249,8 @@ var craftComponents = {
38142
38249
  Markdown: {
38143
38250
  props: exports_external.object({
38144
38251
  content: exports_external.string(),
38145
- color: exports_external.nullable(exports_external.string())
38252
+ color: exports_external.nullable(exports_external.string()),
38253
+ className: classNameProp
38146
38254
  }),
38147
38255
  description: "Renders markdown text with prose styling. Supports bold, italic, code, links, and lists. Use color for custom text color.",
38148
38256
  example: {
@@ -38155,7 +38263,8 @@ var craftComponents = {
38155
38263
  items: exports_external.array(exports_external.tuple([exports_external.string(), exports_external.union([exports_external.string(), exports_external.number()])])),
38156
38264
  cols: exports_external.nullable(exports_external.number()),
38157
38265
  color: exports_external.nullable(exports_external.string()),
38158
- bgColor: exports_external.nullable(exports_external.string())
38266
+ bgColor: exports_external.nullable(exports_external.string()),
38267
+ className: classNameProp
38159
38268
  }),
38160
38269
  description: "Grid of labeled stat values. Each item is a [label, value] tuple. Use color for value text color and bgColor for cell background color.",
38161
38270
  example: {
@@ -38180,7 +38289,8 @@ var craftComponents = {
38180
38289
  derivedLabel: exports_external.nullable(exports_external.string()),
38181
38290
  cols: exports_external.nullable(exports_external.number()),
38182
38291
  color: exports_external.nullable(exports_external.string()),
38183
- bgColor: exports_external.nullable(exports_external.string())
38292
+ bgColor: exports_external.nullable(exports_external.string()),
38293
+ className: classNameProp
38184
38294
  }),
38185
38295
  description: "Grid of labeled stat cells that pair an editable base value with a derived/calculated value, such as ability score plus modifier.",
38186
38296
  example: {
@@ -38201,12 +38311,12 @@ var craftComponents = {
38201
38311
  options: exports_external.optional(exports_external.nullable(exports_external.array(exports_external.string()))),
38202
38312
  variant: exports_external.nullable(exports_external.enum(["default", "secondary", "outline", "destructive"])),
38203
38313
  color: exports_external.nullable(exports_external.string()),
38204
- fileTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string())),
38205
- pageTypeSlug: exports_external.optional(exports_external.nullable(exports_external.string())),
38314
+ ...multiReferenceTargetSlugProps,
38206
38315
  showAddNew: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38207
- showAddNewOnHover: exports_external.optional(exports_external.nullable(exports_external.boolean()))
38316
+ showAddNewOnHover: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38317
+ className: classNameProp
38208
38318
  }),
38209
- description: "Wrapped row of badges. Use for tags, abilities, skills, etc. Provide options to show a preset add menu for fixed vocabularies. showAddNew defaults to true; set it false to hide the add control. showAddNewOnHover defaults to true; set it false to keep Add visible on desktop. When color is set, it overrides the variant with a custom background color. For compact reference arrays, provide fileTypeSlug so the rendered view can resolve names and add references with a picker.",
38319
+ description: "Wrapped row of badges. Use for tags, abilities, skills, etc. Provide options to show a preset add menu for fixed vocabularies. showAddNew defaults to true; set it false to hide the add control. showAddNewOnHover defaults to true; set it false to keep Add visible on desktop. When color is set, it overrides the variant with a custom background color. For compact reference arrays, provide fileTypeSlug (or fileTypeSlugs when the field targets multiple file types) so the rendered view can resolve names and add references with a picker; fields whose schema already declares reference targets need no slug prop.",
38210
38320
  example: {
38211
38321
  items: ["Slash", "Block", "Charge"],
38212
38322
  options: ["Slash", "Block", "Charge", "Parry"],
@@ -38227,7 +38337,8 @@ var craftComponents = {
38227
38337
  field: exports_external.string()
38228
38338
  }))),
38229
38339
  showProgress: exports_external.nullable(exports_external.boolean()),
38230
- emptyText: exports_external.nullable(exports_external.string())
38340
+ emptyText: exports_external.nullable(exports_external.string()),
38341
+ className: classNameProp
38231
38342
  }),
38232
38343
  description: "Read-only checklist for arrays of tasks or objectives. Items can be strings or objects. Use labelField, checkedField, and descriptionField to map object fields; metaFields renders extra badges.",
38233
38344
  example: {
@@ -38287,6 +38398,7 @@ var craftComponents = {
38287
38398
  emptyColor: exports_external.nullable(exports_external.string()),
38288
38399
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
38289
38400
  showValue: exports_external.nullable(exports_external.boolean()),
38401
+ className: classNameProp,
38290
38402
  editPath: editPathProp
38291
38403
  }),
38292
38404
  description: `Row of filled/empty dots for small resource pools — spell slots, ki points, uses per rest, condition pips. Bind value to a number field; when the field is editable, clicking a dot sets the value (clicking the topmost filled dot clears it). max is the pool size (defaults to 5, capped at 25 dots) and can also bind to a field. color sets filled dots, emptyColor unfilled dots (any CSS color value). showValue displays "value / max" text. Values bound to expression fields render read-only unless editPath provides a stored write target. ${EDIT_PATH_DOC}`,
@@ -38310,6 +38422,7 @@ var craftComponents = {
38310
38422
  showMax: exports_external.nullable(exports_external.boolean()),
38311
38423
  color: exports_external.nullable(exports_external.string()),
38312
38424
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
38425
+ className: classNameProp,
38313
38426
  editPath: editPathProp
38314
38427
  }),
38315
38428
  description: `Numeric counter with −/+ buttons for one-tap turn-time edits — hit points, ammo, sorcery points, momentum. Bind value to a number field; each tap saves immediately. min defaults to 0 (set a negative min to allow values below zero), max is uncapped when null and can bind to a field (e.g. {"$state": "/hitPointMax"}), step defaults to 1. showMax renders "23 / 40". The value is also click-to-type editable. ${EDIT_PATH_DOC}`,
@@ -38332,6 +38445,7 @@ var craftComponents = {
38332
38445
  onLabel: exports_external.nullable(exports_external.string()),
38333
38446
  offLabel: exports_external.nullable(exports_external.string()),
38334
38447
  color: exports_external.nullable(exports_external.string()),
38448
+ className: classNameProp,
38335
38449
  editPath: editPathProp
38336
38450
  }),
38337
38451
  description: `Single boolean as a one-tap control — inspiration, concentration, death-save boxes, armor-used flags. Bind value to a boolean field; clicking flips and saves it. variant: "switch" (default), "checkbox", or "chip" (a lit/dim pill; onLabel/offLabel override the pill text per state). ${EDIT_PATH_DOC}`,
@@ -38355,6 +38469,7 @@ var craftComponents = {
38355
38469
  color: exports_external.nullable(exports_external.string()),
38356
38470
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
38357
38471
  showValue: exports_external.nullable(exports_external.boolean()),
38472
+ className: classNameProp,
38358
38473
  editPath: editPathProp
38359
38474
  }),
38360
38475
  description: `Row of square boxes for health/stress/harm tracks. Two modes: bind value to a number field for a filled-count track (click box N to set, click the topmost filled box to clear), or bind states to a string-array field for multi-state tracks where clicking a box cycles empty → each stateOption → empty (e.g. World of Darkness bashing/lethal/aggravated). stateOptions is [{label, symbol}]; the stored array holds labels. boxes is the track length (default 10, cap 30). groupEvery adds a visual gap every N boxes (e.g. 3 for Blades stress). ${EDIT_PATH_DOC}`,
@@ -38386,7 +38501,8 @@ var craftComponents = {
38386
38501
  })
38387
38502
  ]))),
38388
38503
  label: exports_external.nullable(exports_external.string()),
38389
- color: exports_external.nullable(exports_external.string())
38504
+ color: exports_external.nullable(exports_external.string()),
38505
+ className: classNameProp
38390
38506
  }),
38391
38507
  description: "Always-visible roster of toggle chips bound to a string-array field — conditions (poisoned, prone, grappled), exhaustion, status effects. Active chips are lit, inactive ones dim; clicking toggles membership and saves. Unlike BadgeList, the full option set stays visible for at-a-glance combat state. options entries are strings or {label, value?, color?}.",
38392
38508
  example: {
@@ -38405,6 +38521,7 @@ var craftComponents = {
38405
38521
  emptyColor: exports_external.nullable(exports_external.string()),
38406
38522
  size: exports_external.nullable(exports_external.enum(["sm", "md", "lg"])),
38407
38523
  showValue: exports_external.nullable(exports_external.boolean()),
38524
+ className: classNameProp,
38408
38525
  editPath: editPathProp
38409
38526
  }),
38410
38527
  description: `Segmented circular progress clock (Blades in the Dark style) for project clocks, countdowns, and faction progress. Bind value to a number field; clicking a segment fills up to it, clicking the last filled segment clears it. segments is 2-12 (default 4). color fills segments, emptyColor the unfilled ones. ${EDIT_PATH_DOC}`,
@@ -38427,7 +38544,8 @@ var craftComponents = {
38427
38544
  type: exports_external.nullable(exports_external.enum(["string", "number", "boolean"]))
38428
38545
  })),
38429
38546
  label: exports_external.nullable(exports_external.string()),
38430
- emptyText: exports_external.nullable(exports_external.string())
38547
+ emptyText: exports_external.nullable(exports_external.string()),
38548
+ className: classNameProp
38431
38549
  }),
38432
38550
  description: 'Table bound to an array of objects with declared columns — spell lists, inventories with quantity/weight, attack tables. Each column maps a field (dot paths supported, e.g. "item.name"); cells edit inline and save back to that row when the array is a stored field — rows of a computed (expression) array render read-only. Use type: "number" for numeric columns. For arrays of file references prefer FileReferenceGrid; DataTable is for structured plain-object rows.',
38433
38551
  example: {
@@ -38448,7 +38566,8 @@ var craftComponents = {
38448
38566
  props: exports_external.object({
38449
38567
  items: exports_external.array(exports_external.tuple([exports_external.string(), exports_external.string()])),
38450
38568
  labelColor: exports_external.nullable(exports_external.string()),
38451
- valueColor: exports_external.nullable(exports_external.string())
38569
+ valueColor: exports_external.nullable(exports_external.string()),
38570
+ className: classNameProp
38452
38571
  }),
38453
38572
  description: "Vertical list of labeled field/value pairs. Use labelColor and valueColor for custom text colors.",
38454
38573
  example: {
@@ -38466,7 +38585,8 @@ var craftComponents = {
38466
38585
  src: exports_external.nullable(exports_external.string()),
38467
38586
  title: exports_external.nullable(exports_external.string()),
38468
38587
  subtitle: exports_external.nullable(exports_external.string()),
38469
- aspectRatio: aspectRatioEnum
38588
+ aspectRatio: aspectRatioEnum,
38589
+ className: classNameProp
38470
38590
  }),
38471
38591
  description: 'Hero image with optional text overlay. Always renders a gradient fallback when no image is set. aspectRatio defaults to "video" (16:9); also supports "square" (1:1), "portrait" (3:4), "tall" (9:16), "ultrawide" (32:9) for slim banner rows, and "ultratall" (9:32) for narrow full-height columns.',
38472
38592
  example: {
@@ -38480,7 +38600,8 @@ var craftComponents = {
38480
38600
  props: exports_external.object({
38481
38601
  value: exports_external.union([exports_external.string(), exports_external.number()]),
38482
38602
  format: exports_external.nullable(exports_external.enum(["date", "relative", "distance"])),
38483
- color: exports_external.nullable(exports_external.string())
38603
+ color: exports_external.nullable(exports_external.string()),
38604
+ className: classNameProp
38484
38605
  }),
38485
38606
  description: 'Formatted date display. "date" shows "April 29, 2024", "relative" shows "yesterday", "distance" shows "2 days ago". Use color for custom text color.',
38486
38607
  example: { value: 1714400000000, format: "distance", color: null }
@@ -38490,7 +38611,8 @@ var craftComponents = {
38490
38611
  value: exports_external.number(),
38491
38612
  style: exports_external.nullable(exports_external.enum(["decimal", "currency", "percent"])),
38492
38613
  currency: exports_external.nullable(exports_external.string()),
38493
- color: exports_external.nullable(exports_external.string())
38614
+ color: exports_external.nullable(exports_external.string()),
38615
+ className: classNameProp
38494
38616
  }),
38495
38617
  description: 'Formatted number display. "decimal" shows "1,234.56", "currency" shows "$1,234.56", "percent" shows "85%". Use color for custom text color.',
38496
38618
  example: {
@@ -38509,9 +38631,10 @@ var craftComponents = {
38509
38631
  label: exports_external.string(),
38510
38632
  value: exports_external.union([exports_external.string(), exports_external.number()])
38511
38633
  }))),
38512
- ...referenceTargetSlugProps
38634
+ ...multiReferenceTargetSlugProps,
38635
+ className: classNameProp
38513
38636
  }),
38514
- description: "Dropdown select. Provide static options array, a fileTypeSlug to populate from files of that type, or both. value is the selected option's value or an object with referenceId plus a readable name.",
38637
+ description: "Dropdown select. Provide static options array, a fileTypeSlug to populate from files of that type (or fileTypeSlugs when the field targets multiple file types), or both. value is the selected option's value or an object with referenceId plus a readable name. Fields whose schema already declares reference targets need no slug prop.",
38515
38638
  example: {
38516
38639
  value: "fighter",
38517
38640
  label: "Class",
@@ -38527,7 +38650,8 @@ var craftComponents = {
38527
38650
  value: exports_external.nullable(exports_external.string()),
38528
38651
  label: exports_external.nullable(exports_external.string()),
38529
38652
  placeholder: exports_external.nullable(exports_external.string()),
38530
- showLabel: exports_external.optional(exports_external.nullable(exports_external.boolean()))
38653
+ showLabel: exports_external.optional(exports_external.nullable(exports_external.boolean())),
38654
+ className: classNameProp
38531
38655
  }),
38532
38656
  description: 'Voice picker for character dialogue. Bind value to a string field with format "voice". A blank value inherits the GM voice, and the web renderer shows preview controls for each voice. showLabel defaults to true; set it false to hide the label.',
38533
38657
  example: {
@@ -38539,7 +38663,7 @@ var craftComponents = {
38539
38663
  },
38540
38664
  FileReferenceGrid: {
38541
38665
  props: fileReferenceGridProps,
38542
- description: 'Grid of cards showing referenced files with their images and titles. Provide items (array of file reference ids, slugs, names, reference objects, or objects), fileTypeSlug to identify the file type, and referenceField for object arrays (e.g. "item" for [{item: {referenceId, name}, quantity: 2}]). Metadata fields like quantity render as badges. emptyState customizes the full-width empty message and defaults to "None.". showAddNew defaults to true; set it false to hide the add control. showAddNewOnHover defaults to true; set it false to keep Add visible on desktop. addNewVariant defaults to "inline" and accepts "aside" to place one compact add control beside the grid label. imageAspectRatio overrides the card image ratio: "video" (16:9), "square" (1:1), "portrait" (3:4), "tall" (9:16), "ultrawide" (32:9), "ultratall" (9:32, e.g. tall party-roster portraits).',
38666
+ description: `Grid of cards showing referenced files with their images and titles. Provide items (array of file reference ids, slugs, names, reference objects, or objects), fileTypeSlug to identify the file type — or fileTypeSlugs listing every target type for multi-type reference fields (the add picker then offers per-type filtering and stores qualified "type/slug" values) — and referenceField for object arrays (e.g. "item" for [{item: {referenceId, name}, quantity: 2}]). Fields whose schema already declares reference targets need no slug prop. Metadata fields like quantity render as badges. titlePosition moves each card's name: "bottom" (default, below the image), "top" (above the image), "overlay" (over the lower edge of the image), or "hidden". metadataPosition moves the metadata badges: "inline" (default, beside the name), "below" (their own wrapping row under the name — fits longer text values), or "hidden". metadataFields entries accept hidden: true to keep a field editable in the card's edit dialog without displaying it, and label to rename it there; omitting a field from the list removes it from display, editing, and new-row defaults alike. emptyState customizes the full-width empty message and defaults to "None.". showAddNew defaults to true; set it false to hide the add control. showAddNewOnHover defaults to true; set it false to keep Add visible on desktop. addNewVariant defaults to "inline" and accepts "aside" to place one compact add control beside the grid label. imageAspectRatio overrides the card image ratio: "video" (16:9), "square" (1:1), "portrait" (3:4), "tall" (9:16), "ultrawide" (32:9), "ultratall" (9:32, e.g. tall party-roster portraits).`,
38543
38667
  example: {
38544
38668
  items: [
38545
38669
  {
@@ -38594,7 +38718,8 @@ var craftComponents = {
38594
38718
  display: exports_external.nullable(exports_external.enum(["bar", "fraction", "badges", "text"]))
38595
38719
  })),
38596
38720
  conditionsField: exports_external.nullable(exports_external.string()),
38597
- notesField: exports_external.nullable(exports_external.string())
38721
+ notesField: exports_external.nullable(exports_external.string()),
38722
+ className: classNameProp
38598
38723
  }),
38599
38724
  description: 'Turn order tracker for encounters/combat. Displays a sorted list of combatants with configurable stats. Each combatant has a "type" field: "character" (stats pulled live from a character page via "page" VFS path and pageFieldMapping) or "monster" (all stats inline — hp, maxHp, ac, etc. — with optional "statBlockPage" for reference only). pageFieldMapping maps combatant field names to character page content fields (e.g. {"hp": "hitPoints"}). Special values: "$title" for page title, "$coverImage" for page image. stats defines which columns to show.',
38600
38725
  example: {
@@ -38643,12 +38768,12 @@ var craftComponents = {
38643
38768
  };
38644
38769
  var craftCatalog = defineCatalog(schema, {
38645
38770
  components: {
38646
- Separator,
38647
- Accordion,
38648
- Collapsible,
38649
- Avatar,
38650
- Alert,
38651
- Table,
38771
+ Separator: withClassNameProp(Separator),
38772
+ Accordion: withClassNameProp(Accordion),
38773
+ Collapsible: withClassNameProp(Collapsible),
38774
+ Avatar: withClassNameProp(Avatar),
38775
+ Alert: withClassNameProp(Alert),
38776
+ Table: withClassNameProp(Table),
38652
38777
  ...extendedShadcnComponents,
38653
38778
  ...craftComponents
38654
38779
  },
@@ -40424,7 +40549,7 @@ function printLines(lines) {
40424
40549
  }
40425
40550
 
40426
40551
  // src/commands/check.ts
40427
- var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
40552
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
40428
40553
  async function runCheck(argv) {
40429
40554
  const args = parseCommandArgs({
40430
40555
  argv,
@@ -40671,7 +40796,7 @@ function checkReferenceIds({
40671
40796
  const malformed = [];
40672
40797
  for (const file2 of scan.files) {
40673
40798
  const blockId = file2.craftBlock?.referenceId;
40674
- if (blockId !== undefined && !UUID_RE2.test(blockId)) {
40799
+ if (blockId !== undefined && !UUID_RE.test(blockId)) {
40675
40800
  malformed.push(`${file2.path} ($craft.referenceId is not a UUID)`);
40676
40801
  continue;
40677
40802
  }
@@ -40907,7 +41032,7 @@ function isReferenceValue(record2) {
40907
41032
  return false;
40908
41033
  }
40909
41034
  if ("referenceId" in record2) {
40910
- return typeof record2.referenceId === "string" && UUID_RE2.test(record2.referenceId);
41035
+ return typeof record2.referenceId === "string" && UUID_RE.test(record2.referenceId);
40911
41036
  }
40912
41037
  return nonBlankString(record2.slug);
40913
41038
  }
@@ -41900,7 +42025,7 @@ function describeRemoteSource(resolved) {
41900
42025
  // package.json
41901
42026
  var package_default = {
41902
42027
  name: "@craftrpgs/cli",
41903
- version: "0.1.6",
42028
+ version: "0.1.7",
41904
42029
  description: "Sync Craft projects with a local folder — clone, edit with any coding agent, push back.",
41905
42030
  license: "UNLICENSED",
41906
42031
  homepage: "https://craftrpgs.com",
@@ -41927,7 +42052,7 @@ var package_default = {
41927
42052
  "@craft/flow": "workspace:*",
41928
42053
  "@craft/shared": "workspace:*",
41929
42054
  fflate: "^0.8.3",
41930
- typescript: "5.9.3",
42055
+ typescript: "7.0.2",
41931
42056
  uuid: "11.1.0",
41932
42057
  zod: "4.3.6"
41933
42058
  }
@@ -42039,6 +42164,16 @@ function createCliClient({
42039
42164
  if (response.status === 401) {
42040
42165
  throw new CliError(UNAUTHORIZED_HELP, 2);
42041
42166
  }
42167
+ if (response.status === 426) {
42168
+ const { code, message, details } = await readErrorBody(response);
42169
+ throw new CliApiError({
42170
+ message,
42171
+ code: code ?? "cli_outdated",
42172
+ status: response.status,
42173
+ exitCode: 2,
42174
+ details
42175
+ });
42176
+ }
42042
42177
  return response;
42043
42178
  }
42044
42179
  async function parseJson(response, schema2, what) {
@@ -43835,7 +43970,7 @@ var SYSTEM_ACCOUNT_IDS = new Set(Object.values(SYSTEM_ACCOUNTS));
43835
43970
  import { randomUUID } from "node:crypto";
43836
43971
  var LEADING_SLASH_RE = /^\//;
43837
43972
  var CONTENT_EXTENSION_RE = /\.(json|md)$/i;
43838
- var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
43973
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
43839
43974
  function entryName(entry) {
43840
43975
  const livePath = entry.movedTo ?? entry.path;
43841
43976
  const base = (livePath.split("/").at(-1) ?? livePath).replace(CONTENT_EXTENSION_RE, "");
@@ -43871,8 +44006,12 @@ function resolveLink({
43871
44006
  if (!trimmed) {
43872
44007
  return { status: "unknown" };
43873
44008
  }
43874
- const fromEntry = (entry) => entry.fileId ? { status: "resolved", referenceId: entry.referenceId } : { status: "unpushed" };
43875
- if (UUID_RE3.test(trimmed)) {
44009
+ const fromEntry = (entry) => entry.fileId ? {
44010
+ status: "resolved",
44011
+ referenceId: entry.referenceId,
44012
+ name: entryName(entry)
44013
+ } : { status: "unpushed" };
44014
+ if (UUID_RE2.test(trimmed)) {
43876
44015
  const lowered = trimmed.toLowerCase();
43877
44016
  const byReference = ledger.files.find((entry) => entry.referenceId.toLowerCase() === lowered);
43878
44017
  if (byReference) {
@@ -44230,7 +44369,7 @@ async function runImageGenerate(argv) {
44230
44369
  styleLine = "Style: none stored — set settings.imageStyleInstructions in .craft/project.json for consistent art.";
44231
44370
  }
44232
44371
  printLines([
44233
- `Generated image (${result.model}, ${result.size ?? "default size"}, ${result.unitsSpent} ${result.currency}):`,
44372
+ `Generated image (${result.model}, ${result.size ?? "default size"}, ${toDisplay(result.unitsSpent)} ${result.currency}):`,
44234
44373
  ` ${result.image.url ?? "<no url returned>"}`,
44235
44374
  styleLine,
44236
44375
  "",
@@ -44271,7 +44410,7 @@ function toActionableError(error48, operation = "generation") {
44271
44410
  if (currency === "credits") {
44272
44411
  return actionable("Not enough credits to generate this image. Buy credits in Craft, then retry.", 1);
44273
44412
  }
44274
- return actionable("Not enough energy to generate this image. Wait for your daily energy to refill, " + "or retry with --credits to spend purchased credits instead.", 1);
44413
+ return actionable("Not enough energy to generate this image. Retry with --credits to spend " + "purchased credits, or subscribe in Craft for a daily energy allowance.", 2);
44275
44414
  }
44276
44415
  if (error48.code === "validation_error" && error48.status === 422) {
44277
44416
  return actionable(`The provider refused this generation: ${error48.message}. ` + "Adjust the prompt (or reference images) and retry.", 1);
@@ -45668,6 +45807,123 @@ ${remedies.join(`
45668
45807
 
45669
45808
  // src/commands/meta.ts
45670
45809
  import { readFile as readFile11 } from "node:fs/promises";
45810
+
45811
+ // ../shared/src/game-starts/authoring.ts
45812
+ function speakerNameForLink(link) {
45813
+ const base = link.split("/").filter(Boolean).at(-1);
45814
+ const name = base?.split(".")[0]?.trim();
45815
+ return name || undefined;
45816
+ }
45817
+ function isRecord14(value) {
45818
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45819
+ }
45820
+ function translateOpening(opening, where, resolveLink2) {
45821
+ if (!isRecord14(opening)) {
45822
+ return opening;
45823
+ }
45824
+ const openingInput = { ...opening };
45825
+ if (!Array.isArray(openingInput.beats)) {
45826
+ return openingInput;
45827
+ }
45828
+ const beats = openingInput.beats.map((beat, index) => {
45829
+ if (!isRecord14(beat)) {
45830
+ return beat;
45831
+ }
45832
+ const beatInput = { ...beat };
45833
+ if (typeof beatInput.id !== "string") {
45834
+ beatInput.id = crypto.randomUUID();
45835
+ }
45836
+ const speaker = beatInput.speaker;
45837
+ const context = {
45838
+ where: `${where}.beats[${index}].speaker`,
45839
+ expects: "character"
45840
+ };
45841
+ if (speaker === "gm") {
45842
+ beatInput.speaker = { kind: "gm" };
45843
+ } else if (typeof speaker === "string") {
45844
+ const resolved = resolveLink2(speaker, context);
45845
+ beatInput.speaker = {
45846
+ kind: "character",
45847
+ fileReferenceId: resolved?.referenceId ?? null,
45848
+ name: resolved?.name ?? speakerNameForLink(speaker)
45849
+ };
45850
+ } else if (isRecord14(speaker) && "character" in speaker) {
45851
+ const { character, ...speakerRest } = speaker;
45852
+ const resolved = resolveLink2(character, {
45853
+ ...context,
45854
+ where: `${context.where}.character`
45855
+ });
45856
+ beatInput.speaker = {
45857
+ kind: "character",
45858
+ name: resolved?.name ?? (typeof character === "string" ? speakerNameForLink(character) : undefined),
45859
+ ...speakerRest,
45860
+ fileReferenceId: resolved?.referenceId ?? null
45861
+ };
45862
+ }
45863
+ return beatInput;
45864
+ });
45865
+ openingInput.beats = beats;
45866
+ if (typeof openingInput.message !== "string" && openingInput.kind === "static") {
45867
+ const projectable = beats.filter((beat) => isRecord14(beat) && typeof beat.text === "string");
45868
+ openingInput.message = projectable.map((beat) => {
45869
+ const speaker = isRecord14(beat.speaker) ? beat.speaker : undefined;
45870
+ const name = speaker && speaker.kind === "character" && typeof speaker.name === "string" ? speaker.name : null;
45871
+ return name ? `${name}: ${beat.text}` : beat.text;
45872
+ }).join(`
45873
+
45874
+ `).slice(0, GAME_START_OPENING_MAX_LENGTH);
45875
+ }
45876
+ return openingInput;
45877
+ }
45878
+ function translateGameStartAuthoringInput({
45879
+ raw,
45880
+ resolveLink: resolveLink2
45881
+ }) {
45882
+ const input = { ...raw };
45883
+ if ("startingLocation" in input) {
45884
+ input.startingLocationFileReferenceId = resolveLink2(input.startingLocation, {
45885
+ where: "startingLocation",
45886
+ expects: "location"
45887
+ })?.referenceId ?? null;
45888
+ input.startingLocation = undefined;
45889
+ }
45890
+ if (Array.isArray(input.startingCharacters)) {
45891
+ input.startingCharacters = input.startingCharacters.map((character, index) => {
45892
+ if (typeof character === "string") {
45893
+ return {
45894
+ fileReferenceId: resolveLink2(character, {
45895
+ where: `startingCharacters[${index}]`,
45896
+ expects: "character"
45897
+ })?.referenceId ?? null
45898
+ };
45899
+ }
45900
+ return character;
45901
+ });
45902
+ }
45903
+ if ("opening" in input) {
45904
+ input.opening = translateOpening(input.opening, "opening", resolveLink2);
45905
+ }
45906
+ if (Array.isArray(input.characterOpenings)) {
45907
+ input.characterOpenings = input.characterOpenings.map((characterOpening, index) => {
45908
+ if (!isRecord14(characterOpening)) {
45909
+ return characterOpening;
45910
+ }
45911
+ const entry = { ...characterOpening };
45912
+ if ("character" in entry) {
45913
+ entry.characterFileReferenceId = resolveLink2(entry.character, {
45914
+ where: `characterOpenings[${index}].character`,
45915
+ expects: "character"
45916
+ })?.referenceId ?? null;
45917
+ entry.character = undefined;
45918
+ }
45919
+ entry.opening = translateOpening(entry.opening, `characterOpenings[${index}].opening`, resolveLink2);
45920
+ return entry;
45921
+ });
45922
+ }
45923
+ return input;
45924
+ }
45925
+
45926
+ // src/commands/meta.ts
45671
45927
  var META_USAGE = `Usage: craft meta get <path> [--json]
45672
45928
  ` + ` craft meta set-game-start <game-start-path> --file <json>
45673
45929
  ` + ` craft meta set-gm-trigger <gm-instruction-path> (--file <json> | --clear)
@@ -45772,112 +46028,29 @@ function projectOpeningForOutput(workspace, opening) {
45772
46028
  }))
45773
46029
  };
45774
46030
  }
45775
- function speakerNameForLink(link) {
45776
- const base = link.split("/").filter(Boolean).at(-1);
45777
- const name = base?.split(".")[0]?.trim();
45778
- return name || undefined;
45779
- }
45780
46031
  function translateGameStartInput(workspace, raw) {
45781
46032
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
45782
46033
  throw new CliError("The game start JSON must be a single object.", 2);
45783
46034
  }
45784
- const input = { ...raw };
45785
46035
  const failures = [];
45786
- const resolve6 = (link, where) => {
45787
- if (link === null || link === undefined) {
45788
- return null;
45789
- }
45790
- if (typeof link !== "string") {
45791
- failures.push(`${where}: expected a path/slug string`);
45792
- return null;
45793
- }
45794
- const resolution = resolveLink({ ledger: workspace.ledger, link });
45795
- if (resolution.status === "resolved") {
45796
- return resolution.referenceId;
45797
- }
45798
- failures.push(resolution.status === "unpushed" ? `${where}: "${link}" has not been pushed yet — push it first` : `${where}: "${link}" does not resolve to a tracked file`);
45799
- return null;
45800
- };
45801
- const translateOpening = (opening, where) => {
45802
- if (typeof opening !== "object" || opening === null) {
45803
- return opening;
45804
- }
45805
- const openingInput = { ...opening };
45806
- if (!Array.isArray(openingInput.beats)) {
45807
- return openingInput;
45808
- }
45809
- const beats = openingInput.beats.map((beat, index) => {
45810
- if (typeof beat !== "object" || beat === null) {
45811
- return beat;
46036
+ const input = translateGameStartAuthoringInput({
46037
+ raw: { ...raw },
46038
+ resolveLink: (link, { where }) => {
46039
+ if (link === null || link === undefined) {
46040
+ return null;
45812
46041
  }
45813
- const beatInput = { ...beat };
45814
- if (typeof beatInput.id !== "string") {
45815
- beatInput.id = crypto.randomUUID();
46042
+ if (typeof link !== "string") {
46043
+ failures.push(`${where}: expected a path/slug string`);
46044
+ return null;
45816
46045
  }
45817
- const speaker = beatInput.speaker;
45818
- if (typeof speaker === "string" && speaker !== "gm") {
45819
- beatInput.speaker = {
45820
- kind: "character",
45821
- fileReferenceId: resolve6(speaker, `${where}.beats[${index}].speaker`),
45822
- name: speakerNameForLink(speaker)
45823
- };
45824
- } else if (speaker === "gm") {
45825
- beatInput.speaker = { kind: "gm" };
45826
- } else if (typeof speaker === "object" && speaker !== null && "character" in speaker) {
45827
- const { character, ...speakerRest } = speaker;
45828
- beatInput.speaker = {
45829
- kind: "character",
45830
- name: typeof character === "string" ? speakerNameForLink(character) : undefined,
45831
- ...speakerRest,
45832
- fileReferenceId: resolve6(character, `${where}.beats[${index}].speaker.character`)
45833
- };
46046
+ const resolution = resolveLink({ ledger: workspace.ledger, link });
46047
+ if (resolution.status === "resolved") {
46048
+ return { referenceId: resolution.referenceId, name: resolution.name };
45834
46049
  }
45835
- return beatInput;
45836
- });
45837
- openingInput.beats = beats;
45838
- if (typeof openingInput.message !== "string" && openingInput.kind === "static") {
45839
- const projectable = beats.filter((beat) => typeof beat === "object" && beat !== null && typeof beat.text === "string");
45840
- openingInput.message = projectable.map((beat) => {
45841
- const speaker = beat.speaker;
45842
- const name = speaker && speaker.kind === "character" && typeof speaker.name === "string" ? speaker.name : null;
45843
- return name ? `${name}: ${beat.text}` : beat.text;
45844
- }).join(`
45845
-
45846
- `).slice(0, GAME_START_OPENING_MAX_LENGTH);
46050
+ failures.push(resolution.status === "unpushed" ? `${where}: "${link}" has not been pushed yet — push it first` : `${where}: "${link}" does not resolve to a tracked file`);
46051
+ return null;
45847
46052
  }
45848
- return openingInput;
45849
- };
45850
- if ("startingLocation" in input) {
45851
- input.startingLocationFileReferenceId = resolve6(input.startingLocation, "startingLocation");
45852
- input.startingLocation = undefined;
45853
- }
45854
- if (Array.isArray(input.startingCharacters)) {
45855
- input.startingCharacters = input.startingCharacters.map((character, index) => {
45856
- if (typeof character === "string") {
45857
- return {
45858
- fileReferenceId: resolve6(character, `startingCharacters[${index}]`)
45859
- };
45860
- }
45861
- return character;
45862
- });
45863
- }
45864
- if ("opening" in input) {
45865
- input.opening = translateOpening(input.opening, "opening");
45866
- }
45867
- if (Array.isArray(input.characterOpenings)) {
45868
- input.characterOpenings = input.characterOpenings.map((characterOpening, index) => {
45869
- if (typeof characterOpening !== "object" || characterOpening === null) {
45870
- return characterOpening;
45871
- }
45872
- const entry = { ...characterOpening };
45873
- if ("character" in entry) {
45874
- entry.characterFileReferenceId = resolve6(entry.character, `characterOpenings[${index}].character`);
45875
- entry.character = undefined;
45876
- }
45877
- entry.opening = translateOpening(entry.opening, `characterOpenings[${index}].opening`);
45878
- return entry;
45879
- });
45880
- }
46053
+ });
45881
46054
  if (failures.length > 0) {
45882
46055
  throw new CliError(`Nothing was saved — some references do not resolve to pushed files:
45883
46056
  ` + `${failures.map((f) => ` ${f}`).join(`
@@ -46209,7 +46382,7 @@ function escapeRegExp(value) {
46209
46382
  // src/commands/project-completeness.ts
46210
46383
  var TRIVIAL_DESCRIPTION_LENGTH = 40;
46211
46384
  var CROSS_LINK_FILE_THRESHOLD = 5;
46212
- var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46385
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46213
46386
  var SCRATCH_NAME_RE = /\b(?:test|asdf|scratch|repro|tmp|todo|example)\b/i;
46214
46387
  async function runProjectCompleteness(argv) {
46215
46388
  const args = parseCommandArgs({
@@ -46575,7 +46748,7 @@ function isReferenceValue2(record2) {
46575
46748
  return false;
46576
46749
  }
46577
46750
  if ("referenceId" in record2) {
46578
- return typeof record2.referenceId === "string" && UUID_RE4.test(record2.referenceId);
46751
+ return typeof record2.referenceId === "string" && UUID_RE3.test(record2.referenceId);
46579
46752
  }
46580
46753
  return nonBlankString(record2.slug);
46581
46754
  }
@@ -46981,7 +47154,7 @@ async function removeStaleManagedSkills({
46981
47154
  // src/engine/pull.ts
46982
47155
  import { mkdir as mkdir6, rename as rename4, rm as rm7, rmdir } from "node:fs/promises";
46983
47156
  import { dirname as dirname7 } from "node:path";
46984
- function isRecord14(value) {
47157
+ function isRecord15(value) {
46985
47158
  return typeof value === "object" && value !== null && !Array.isArray(value);
46986
47159
  }
46987
47160
  function serializeContentForDisk({
@@ -46994,9 +47167,9 @@ function serializeContentForDisk({
46994
47167
  const craftBlock = referenceId ? craftBlockForEmbedding({
46995
47168
  referenceId,
46996
47169
  ...settings ? { settings } : {},
46997
- ...isRecord14(metadata) ? { metadata } : {}
47170
+ ...isRecord15(metadata) ? { metadata } : {}
46998
47171
  }) : null;
46999
- if (contentType === "markdown" && isRecord14(content)) {
47172
+ if (contentType === "markdown" && isRecord15(content)) {
47000
47173
  return serializeMarkdown({
47001
47174
  name: typeof content.name === "string" ? content.name : undefined,
47002
47175
  description: typeof content.description === "string" ? content.description : undefined,
@@ -47004,7 +47177,7 @@ function serializeContentForDisk({
47004
47177
  content: typeof content.content === "string" ? content.content : ""
47005
47178
  }, { craftBlock });
47006
47179
  }
47007
- if (craftBlock && isRecord14(content)) {
47180
+ if (craftBlock && isRecord15(content)) {
47008
47181
  return `${JSON.stringify({
47009
47182
  [CRAFT_BLOCK_KEY]: craftBlock,
47010
47183
  ...stripCraftBlock(content)
@@ -47019,7 +47192,7 @@ function contentTypeForPath2(path) {
47019
47192
  }
47020
47193
  function applyMetaBase(entry, body) {
47021
47194
  entry.settings = craftFileSettingsFromWire(body.settings);
47022
- entry.metadataHash = isRecord14(body.metadata) ? hashCanonicalJson(body.metadata) : undefined;
47195
+ entry.metadataHash = isRecord15(body.metadata) ? hashCanonicalJson(body.metadata) : undefined;
47023
47196
  }
47024
47197
  function emptyResult() {
47025
47198
  return {
@@ -47908,7 +48081,7 @@ import { join as join7 } from "node:path";
47908
48081
 
47909
48082
  // src/engine/plan.ts
47910
48083
  var MAX_CLI_PUSH_OPS = 500;
47911
- function isRecord15(value) {
48084
+ function isRecord16(value) {
47912
48085
  return typeof value === "object" && value !== null && !Array.isArray(value);
47913
48086
  }
47914
48087
  var PATHSPEC_LEADING_DOT_SLASH = /^\.\//;
@@ -48029,7 +48202,6 @@ function buildPushPlan(input) {
48029
48202
  contentType: local.contentType,
48030
48203
  schema: local.schema,
48031
48204
  ...local.layout != null ? { layout: local.layout } : {},
48032
- ...local.category !== undefined ? { category: local.category } : {},
48033
48205
  designation: local.designation,
48034
48206
  ...local.imageStyleInstructions !== undefined ? { imageStyleInstructions: local.imageStyleInstructions } : {},
48035
48207
  ...local.showCoverImage !== undefined ? { showCoverImage: local.showCoverImage } : {},
@@ -48093,7 +48265,7 @@ function buildPushPlan(input) {
48093
48265
  continue;
48094
48266
  }
48095
48267
  let content = scanned.content;
48096
- if (isRecord15(content) && content.name !== newBaseName.value.baseName) {
48268
+ if (isRecord16(content) && content.name !== newBaseName.value.baseName) {
48097
48269
  content = { ...content, name: newBaseName.value.baseName };
48098
48270
  nameRewrites.push({
48099
48271
  diskPath: toPath,
@@ -50233,7 +50405,7 @@ async function workspaceRemote2() {
50233
50405
 
50234
50406
  // src/commands/type.ts
50235
50407
  import { existsSync as existsSync9 } from "node:fs";
50236
- var TYPE_USAGE = "Usage: craft type new <Name> [--markdown] [--designation <token>] [--category <token>]";
50408
+ var TYPE_USAGE = "Usage: craft type new <Name> [--markdown] [--designation <token>]";
50237
50409
  var STARTER_JSON_SCHEMA = {
50238
50410
  type: "object",
50239
50411
  properties: {},
@@ -50252,7 +50424,6 @@ async function runTypeNew(argv) {
50252
50424
  flags: {
50253
50425
  markdown: { type: "boolean" },
50254
50426
  designation: { type: "string" },
50255
- category: { type: "string" },
50256
50427
  json: { type: "boolean" }
50257
50428
  },
50258
50429
  maxPositionals: 1
@@ -50284,7 +50455,6 @@ async function runTypeNew(argv) {
50284
50455
  name,
50285
50456
  contentType,
50286
50457
  designation,
50287
- category: stringFlag(args, "category") ?? null,
50288
50458
  schema: contentType === "json" ? STARTER_JSON_SCHEMA : null,
50289
50459
  layout: null,
50290
50460
  imageStyleInstructions: null,
@@ -50298,7 +50468,7 @@ async function runTypeNew(argv) {
50298
50468
  `Create files of the type as <Anything>.${typeSlugExtension} anywhere in the workspace.`,
50299
50469
  "Push the new type and its files together with `craft push --include-types`."
50300
50470
  ] : [
50301
- `Markdown types need no schema — edit ${typeFilePath} only if you want a designation or category.`,
50471
+ `Markdown types need no schema — edit ${typeFilePath} only if you want a designation.`,
50302
50472
  `Create files of the type as <Anything>.${typeSlugExtension} anywhere in the workspace.`,
50303
50473
  "Push the new type and its files together with `craft push --include-types`."
50304
50474
  ];
@@ -50440,7 +50610,7 @@ var HELP_COMMAND_LINES = ` clone <projectIdOrUrl> [dir] Download a project as
50440
50610
  resolve <path> Mark a conflict resolved \u2014 your version wins
50441
50611
  checkout <path> Take the remote version of a conflicted file
50442
50612
  mv <from> <to> Rename/move a file, keeping its identity
50443
- type new <Name> [--markdown] [--designation <t>] [--category <t>]
50613
+ type new <Name> [--markdown] [--designation <t>]
50444
50614
  Author a new file type locally (push with --include-types)
50445
50615
  voices [--language <code>] [--gender <t>] [--age <t>]
50446
50616
  List voice ids and traits for characters and the GM
@@ -50701,7 +50871,7 @@ Rename/move a file while keeping its identity (history, references). The
50701
50871
  move is recorded in the ledger and pushed as a move op on the next
50702
50872
  \`craft push\`; editing the file's content before pushing is fine.
50703
50873
  `,
50704
- type: `Usage: craft type new <Name> [--markdown] [--designation <token>] [--category <token>]
50874
+ type: `Usage: craft type new <Name> [--markdown] [--designation <token>]
50705
50875
 
50706
50876
  Scaffold a new file type at .craft/file-types/<slug>.json. Edit its schema,
50707
50877
  create files as <Anything>.<slug>.json, then push everything together with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftrpgs/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Sync Craft projects with a local folder — clone, edit with any coding agent, push back.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://craftrpgs.com",
@@ -24,10 +24,10 @@
24
24
  "typecheck": "tsc --noEmit"
25
25
  },
26
26
  "devDependencies": {
27
- "@craft/flow": "workspace:*",
28
- "@craft/shared": "workspace:*",
27
+ "@craft/flow": "0.0.1",
28
+ "@craft/shared": "0.0.1",
29
29
  "fflate": "^0.8.3",
30
- "typescript": "5.9.3",
30
+ "typescript": "7.0.2",
31
31
  "uuid": "11.1.0",
32
32
  "zod": "4.3.6"
33
33
  }