@vttforge/cli 0.0.0 → 0.5.1

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 (67) hide show
  1. package/CHANGELOG.md +364 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -5
  4. package/dist/bin.d.mts +1 -0
  5. package/dist/bin.mjs +201 -0
  6. package/dist/bin.mjs.map +1 -0
  7. package/dist/index.d.mts +496 -0
  8. package/dist/index.mjs +2 -0
  9. package/dist/src-CWhUNM__.mjs +2443 -0
  10. package/dist/src-CWhUNM__.mjs.map +1 -0
  11. package/package.json +62 -9
  12. package/templates/module-js/.github/workflows/release.yml +92 -0
  13. package/templates/module-js/README.md +57 -0
  14. package/templates/module-js/_gitignore +12 -0
  15. package/templates/module-js/lang/en.json +11 -0
  16. package/templates/module-js/module.json +20 -0
  17. package/templates/module-js/package.json +24 -0
  18. package/templates/module-js/scripts/main.mjs +65 -0
  19. package/templates/module-js/styles/main.css +16 -0
  20. package/templates/module-js/vite.config.mjs +13 -0
  21. package/templates/module-ts/.github/workflows/release.yml +92 -0
  22. package/templates/module-ts/README.md +57 -0
  23. package/templates/module-ts/_gitignore +12 -0
  24. package/templates/module-ts/lang/en.json +11 -0
  25. package/templates/module-ts/module.json +20 -0
  26. package/templates/module-ts/package.json +26 -0
  27. package/templates/module-ts/scripts/foundry-globals.ts +23 -0
  28. package/templates/module-ts/scripts/main.ts +71 -0
  29. package/templates/module-ts/styles/main.css +16 -0
  30. package/templates/module-ts/tsconfig.json +18 -0
  31. package/templates/module-ts/vite.config.mjs +13 -0
  32. package/templates/system-js/.github/workflows/release.yml +92 -0
  33. package/templates/system-js/README.md +57 -0
  34. package/templates/system-js/_gitignore +12 -0
  35. package/templates/system-js/lang/en.json +55 -0
  36. package/templates/system-js/package.json +25 -0
  37. package/templates/system-js/scripts/data/character-data.mjs +79 -0
  38. package/templates/system-js/scripts/data/gear-data.mjs +37 -0
  39. package/templates/system-js/scripts/main.mjs +79 -0
  40. package/templates/system-js/scripts/migrations.mjs +43 -0
  41. package/templates/system-js/scripts/sheets/character-sheet.mjs +164 -0
  42. package/templates/system-js/scripts/sheets/gear-sheet.mjs +65 -0
  43. package/templates/system-js/styles/main.css +495 -0
  44. package/templates/system-js/system.json +44 -0
  45. package/templates/system-js/template.json +8 -0
  46. package/templates/system-js/templates/actor/character-sheet.hbs +168 -0
  47. package/templates/system-js/templates/item/gear-sheet.hbs +54 -0
  48. package/templates/system-js/vite.config.mjs +16 -0
  49. package/templates/system-ts/.github/workflows/release.yml +92 -0
  50. package/templates/system-ts/README.md +57 -0
  51. package/templates/system-ts/_gitignore +12 -0
  52. package/templates/system-ts/lang/en.json +55 -0
  53. package/templates/system-ts/package.json +27 -0
  54. package/templates/system-ts/scripts/data/character-data.ts +81 -0
  55. package/templates/system-ts/scripts/data/gear-data.ts +37 -0
  56. package/templates/system-ts/scripts/foundry-globals.ts +33 -0
  57. package/templates/system-ts/scripts/main.ts +82 -0
  58. package/templates/system-ts/scripts/migrations.ts +43 -0
  59. package/templates/system-ts/scripts/sheets/character-sheet.ts +202 -0
  60. package/templates/system-ts/scripts/sheets/gear-sheet.ts +68 -0
  61. package/templates/system-ts/styles/main.css +495 -0
  62. package/templates/system-ts/system.json +44 -0
  63. package/templates/system-ts/template.json +8 -0
  64. package/templates/system-ts/templates/actor/character-sheet.hbs +168 -0
  65. package/templates/system-ts/templates/item/gear-sheet.hbs +54 -0
  66. package/templates/system-ts/tsconfig.json +18 -0
  67. package/templates/system-ts/vite.config.mjs +16 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,364 @@
1
+ # @vttforge/cli
2
+
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 57af020: Ship the template pins that were already corrected.
8
+
9
+ The templates under `templates/*` name the `@vttforge/*` ranges a scaffolded project starts with. Those pins were kept current in the repo — a guard has caught a stale one eight times — but they were only ever corrected *in the repo*. The templates ride inside this package's tarball, and this package was never released alongside the bumps, so nothing reached anyone.
10
+
11
+ `pnpm create vttforge` has been scaffolding `@vttforge/core@^0.6.0` while core shipped 0.10.0. A new project could not see sheet registration, text enrichers, or `BaseDocumentSheet`, and its lockfile would resolve a core four minors old.
12
+
13
+ Templates now pin `@vttforge/core@^0.10.0` and `@vttforge/vite-plugin@^0.4.0`.
14
+
15
+ The guard also learned the second half: if a release bumps a package the templates pin, it has to include `@vttforge/cli`, or the corrected pin sits in the repo and ships to nobody.
16
+
17
+ ## 0.5.0
18
+
19
+ ### Minor Changes
20
+
21
+ - 3eb8c96: `vttforge init` can now run without a terminal.
22
+
23
+ Five values — id, title, description, author, license — were always asked for, with no flag to supply them. Clack prompts read from stdin, so with none attached the scaffolder hung: no error, no output, nothing to debug. That ruled out CI and scripts, and meant nothing could test the scaffolder through its own entry point.
24
+
25
+ Each now has a flag, and `--yes` (`-y`) takes the default for anything not passed. A run with no terminal assumes `--yes`, so CI needs no extra flag.
26
+
27
+ ```bash
28
+ vttforge init pdf-character-sheet --type module --lang ts \
29
+ --title "PDF Character Sheet" --license Apache-2.0 --yes
30
+ ```
31
+
32
+ The directory name is the one thing with no sensible default. Without a terminal to ask, the run stops and says so.
33
+
34
+ ### Patch Changes
35
+
36
+ - 11187a0: Say what the scaffolded schemas mean.
37
+
38
+ The generated number fields declared `required: true` but left `nullable` alone, so they still accepted `null` — and now that the types say so, that shows up as an error the author has to think about. They set `nullable: false`.
39
+
40
+ The six ability scores share one spec instead of repeating it six times.
41
+ - 353633c: Build each ability score from a factory rather than one shared options object.
42
+
43
+ A field keeps a reference to the options it was handed, and some field classes write back into that object. Six fields sharing one object is safe for the field this template uses, but it is not a habit to teach in code people copy and adapt.
44
+ - c24b2e9: Point the templates at the core version this release publishes.
45
+
46
+ The `ColorField` and presence fixes take `@vttforge/core` to 0.6.0, and a
47
+ caret pins the minor on a 0.x version, so the templates' `^0.5.0` would not
48
+ have matched.
49
+
50
+ ## 0.4.0
51
+
52
+ ### Minor Changes
53
+
54
+ - 17a6b8c: Redraw only the sheets a template change actually affects.
55
+
56
+ A sheet declares its templates in `static PARTS`, so the mapping is on the
57
+ open window itself — no build-time graph needed. Each part names a primary
58
+ `template` and may list further `templates`, which is where a partial it
59
+ pulls in appears. Only the affected parts re-render, so scroll position and
60
+ focus survive everywhere else. When nothing claims the file the whole screen
61
+ still redraws, because a partial can be reached without any part naming it.
62
+
63
+ The watcher now also skips a file whose content did not change. Vite rewrites
64
+ its entire output on every build, so a filesystem event alone means "the
65
+ bundler ran", not "the developer edited this" — and without this the language
66
+ file gets resent untouched on every save, and the JSON path redraws every open
67
+ window, undoing the scoping on the file that did change.
68
+
69
+ Measured in a running world with 21 applications open: editing one sheet
70
+ template went from 35 renders to 1.
71
+
72
+ ## 0.3.0
73
+
74
+ ### Minor Changes
75
+
76
+ - 5b72b4b: `vttforge dev` now applies saves in place, without a page refresh.
77
+
78
+ It opens a small WebSocket, links `@vttforge/dev-module` into Foundry's
79
+ modules directory, and watches the build output — sending a payload per
80
+ changed CSS, template or language file.
81
+
82
+ The socket is hand-written rather than pulled from a package. The CLI ships
83
+ to every consumer, so each dependency is one they install too, and what is
84
+ needed here is a narrow slice of the protocol: accept the upgrade, send
85
+ unmasked text frames, notice when a client leaves.
86
+
87
+ Watching `dist/` rather than hooking into Vite keeps this correct across
88
+ bundler versions, and gives the served path directly — `dist/` is what
89
+ Foundry mounts.
90
+
91
+ Failing to start the bridge costs hot reload, not the dev loop. A busy port
92
+ or a missing companion package prints why and carries on watching.
93
+
94
+ New: `--hmr-port` to move the bridge off 31313.
95
+
96
+ ### Patch Changes
97
+
98
+ - 1fb967d: Close the three soundness gaps `vttforge audit` shipped with, and a fourth
99
+ found while fixing them.
100
+
101
+ - `filePathFields` is an object whose keys are the field paths, not a flat
102
+ array. Reading it as an array dropped every correctly declared path, so
103
+ rule 004 reported declared fields as missing.
104
+ - Modules register subtypes as `<moduleId>.<type>` while the manifest
105
+ declares the bare key. The two were compared verbatim, so no module
106
+ registration ever matched its own declaration.
107
+ - Rule 007 searched every schema in the project for the token attribute.
108
+ Token bars read `actor.system`, so an Item model declaring the same path
109
+ satisfied the check on a system whose bars were in fact broken. The
110
+ narrowing applies only when Actor registrations are known; with none
111
+ found the rule falls back to the previous behaviour.
112
+ - Registrations through bracket notation were invisible to the scanner
113
+ entirely — which is the only form modules can use, since a dot in the key
114
+ rules out property access.
115
+ - c90357e: Point the templates at the CLI version this release publishes.
116
+
117
+ The templates asked for `@vttforge/cli@^0.2.0` while the release takes it to
118
+ 0.3.0. A caret pins the minor on a 0.x version, so that range would not have
119
+ matched — every project scaffolded after the release would ask for a version
120
+ the release had just superseded.
121
+
122
+ ## 0.2.0
123
+
124
+ ### Minor Changes
125
+
126
+ - 9462144: Require Node 26.
127
+
128
+ The floor moves from `>=22.14.0` to `>=26.0.0` across every package and the
129
+ four scaffolding templates, and the bundler target for the Node-side
130
+ packages moves from `node22` to `node26`.
131
+
132
+ Node 22 entered maintenance in October 2025 and receives security fixes
133
+ only. Node 26 becomes the active LTS line on 2026-10-28.
134
+
135
+ This is breaking for anyone on Node 22 or 24. It is marked `minor` rather
136
+ than `major` on purpose: these packages are still on 0.x, where a minor
137
+ signals the break, and a major would push every package to 1.0.0 — a claim
138
+ of API stability that has not been audited, on packages two of which are
139
+ still stubs.
140
+
141
+ The templates move to the versions this release publishes. On 0.x a caret
142
+ pins the minor, so their old ranges would not have matched.
143
+
144
+ CI now pins Node through `actions/setup-node` instead of inheriting whatever
145
+ the runner image ships, so the version the packages declare is the version
146
+ they are tested on. It was not before: the workflow took the image's Node,
147
+ and nothing enforced the declared floor because `engine-strict` is not set.
148
+
149
+ ## 0.1.1
150
+
151
+ ### Patch Changes
152
+
153
+ - 5f03da9: Move to citty 0.2 and cover argument parsing with tests.
154
+
155
+ citty 0.2 is ESM-only, drops its runtime dependencies, and rewrites the
156
+ parser on top of `node:util.parseArgs`. Parsing behaviour is unchanged for
157
+ every flag this CLI defines — negation, aliases, positionals, `--flag=value`
158
+ and boolean defaults all produce identical results on 0.1.6 and 0.2.2.
159
+
160
+ The command tree moves from `bin.ts` to `cli.ts`, leaving the bin as the
161
+ launch only. The definitions were unreachable from tests before, because a
162
+ module that calls `runMain` on import runs the CLI. They are now covered,
163
+ including `--no-install` and `--no-git`, which would have made the
164
+ scaffolder install dependencies for someone who asked it not to had the
165
+ parser rewrite changed them.
166
+
167
+ ## 0.1.0
168
+
169
+ ### Minor Changes
170
+
171
+ - 139802c: feat(cli): ship `vttforge audit` — scan for seven v13 footguns
172
+
173
+ Closes the third and final slice of Track 1. `vttforge audit [path]` scans
174
+ a system or module project root and reports findings against the
175
+ `VTTF-AUDIT-NNN` catalog of v13 manifest + code footguns.
176
+
177
+ **Rules implemented:**
178
+
179
+ | ID | Severity | Scope | What it catches |
180
+ | ---------------- | -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | `VTTF-AUDIT-001` | HIGH | manifest | `flags.hotReload` as array (v12) or missing required `extensions` key. Silently disables HMR. |
182
+ | `VTTF-AUDIT-002` | MEDIUM | manifest | Top-level `gridDistance` / `gridUnits` (v12) instead of `grid: {type, distance, units, diagonals}` (v13). Auto-migrates today, removed in v14. |
183
+ | `VTTF-AUDIT-003` | LOW | manifest | `styles: ["foo.css"]` (string array) instead of `[{src, layer?}]` (v13). Auto-migrates but loses cascade-layer control. |
184
+ | `VTTF-AUDIT-004` | MEDIUM | manifest + source | `HTMLField` / `FilePathField` declared in TypeDataModel schema but missing from `documentTypes.<Doc>.<subtype>.htmlFields` / `.filePathFields`. Server only sanitises declared paths → XSS risk on undeclared. Subtype-aware via `CONFIG.*.dataModels` + `registerSystem({ actorDataModels })` parsing; full-path matching through SchemaField nesting. |
185
+ | `VTTF-AUDIT-005` | MEDIUM | source | `class X extends TypeDataModel` without a `prepareBaseData()` method. Active Effects apply between base and derived; consumers see uninitialised fields. Checked per class. |
186
+ | `VTTF-AUDIT-006` | LOW | source | `_addDataFieldMigrations()` override on a TypeDataModel subclass. Real API is `static migrateData(source)` calling singular `super._addDataFieldMigration(...)`. |
187
+ | `VTTF-AUDIT-007` | MEDIUM | manifest + source | `primaryTokenAttribute` / `secondaryTokenAttribute` doesn't resolve to a `SchemaField({ value, max })` at the exact dot-path. Nested paths like `attributes.hp` are traversed. |
188
+
189
+ **CLI surface:**
190
+
191
+ ```bash
192
+ vttforge audit # scan cwd, print markdown
193
+ vttforge audit ./my-project # scan a different path
194
+ vttforge audit --json # machine-readable JSON for CI piping
195
+ vttforge audit --strict # exit 1 on any finding (default: HIGH only)
196
+ ```
197
+
198
+ **Exit codes:**
199
+ - `0` — clean, or only MEDIUM/LOW findings (advisory mode)
200
+ - `1` — at least one HIGH finding, or any finding in `--strict`
201
+
202
+ Uses `process.exitCode` (not `process.exit`) so piped JSON reports aren't
203
+ truncated on failing CI runs.
204
+
205
+ **Implementation notes:**
206
+ - Pure read-only — never modifies the source tree.
207
+ - Regex-based source scanning. The trade-off is occasional false negatives
208
+ on heavily-formatted code; an AST dependency (TypeScript compiler) would
209
+ add ~30MB to the CLI for seven pattern checks. Rule 007's
210
+ `SchemaField({value, max})` detection uses a balanced-brace scan plus a
211
+ depth-tracking top-level-key extractor to correctly distinguish
212
+ SchemaField siblings from nested constructor options.
213
+ - Rule 005 distinguishes `extends TypeDataModel` (direct, flagged) from
214
+ `extends BaseTypeDataModel()` (VTTForge factory, safe) via the `\b`
215
+ word boundary, and is checked per class so a sibling subclass without
216
+ the hook doesn't hide behind a sibling that has it.
217
+ - Rule 004 takes the FULL schema path through enclosing SchemaField
218
+ wrappers and matches exactly against declared paths (with `system.`
219
+ prefix stripped — Foundry convention).
220
+
221
+ **Tests:** 247 passing (+13 new test files, ~110 new test cases). Integration
222
+ suite scaffolds each of the four templates and runs the audit against the
223
+ output — they must report zero findings, otherwise either a template
224
+ regressed or the audit grew a false positive.
225
+
226
+ - 09de435: feat(cli): ship `vttforge dev` and `vttforge build`
227
+
228
+ `@vttforge/cli` graduates from "scaffold-only" to a full local dev loop.
229
+
230
+ **`vttforge dev`** — single-process dev experience for system/module work:
231
+ 1. Runs `vite build` once to populate `dist/` and emit the manifest.
232
+ 2. Reads the manifest, detects whether the project is a system or module.
233
+ 3. Resolves the Foundry user-data directory via a four-step chain:
234
+ `--data-dir` flag → `FOUNDRY_DATA_DIR` env → project's
235
+ `.vttforge/config.json` → OS default with interactive first-run
236
+ prompt that saves the choice. OS detection honors `XDG_DATA_HOME`
237
+ on Linux and `%LOCALAPPDATA%` on Windows.
238
+ 4. Drops a symlink at `<dataRoot>/Data/<systems|modules>/<id>` pointing
239
+ at the project's `dist/`. Cross-platform — `junction` on Windows so
240
+ the call succeeds without Developer Mode or admin elevation; safety
241
+ rails refuse to overwrite real files/directories.
242
+ 5. Spawns `vite build --watch` with inherited stdio, blocks until
243
+ SIGINT/SIGTERM, then cleans up the symlink and kills vite.
244
+
245
+ When Foundry runs with `--hotReload`, file saves trigger live reload
246
+ without a browser refresh — chokidar follows the symlink and Foundry's
247
+ built-in dispatcher swaps CSS/HBS/JSON in place. No additional client
248
+ wiring needed in this release.
249
+
250
+ **`vttforge build`** — produces a foundryvtt.com-ready release zip:
251
+ 1. Cleans `dist/`, runs `vite build` in production mode.
252
+ 2. Reads the manifest for `id` + `version`.
253
+ 3. Emits `<id>-<version>.zip` at the project root with contents at the
254
+ zip root (no wrapper folder), pulling in `LICENSE`, `README.md`, and
255
+ `CHANGELOG.md` from the project root when present and not already
256
+ inside `dist/`.
257
+
258
+ **Templates updated** — all four (system-ts, system-js, module-ts,
259
+ module-js) now ship `pnpm dev` / `pnpm build` aliased to the new
260
+ commands, drop the manual `ln -s` instructions, and gain
261
+ `@vttforge/cli` as a devDependency. The release workflow continues to
262
+ invoke vite directly so URL injection happens after build but before
263
+ zipping; it now validates tag-derived version strings and passes every
264
+ GitHub-supplied value via `env:` instead of inline interpolation to
265
+ close a command-injection vector that a malicious tag push could
266
+ otherwise exploit.
267
+
268
+ **Internals exposed** — `runDev`, `runBuild`, `emitReleaseZip`,
269
+ `setupDevSymlink`, `resolveFoundryDataDir`, `createLink`/`removeLink`,
270
+ `readManifest`, and `emitZip` are all importable from `@vttforge/cli`
271
+ for consumers building higher-level tooling.
272
+
273
+ **Dependencies** — adds `archiver` (zip emission) pinned to `^7` since
274
+ archiver v8 went ESM-only with renamed exports that DefinitelyTyped
275
+ hasn't caught up to yet.
276
+
277
+ - eb1c41d: feat(cli): ship the `vttforge init` scaffolder + four built-in templates
278
+
279
+ `@vttforge/cli` now provides a real, interactive scaffolder. `vttforge init`
280
+ (or `pnpm create vttforge` via the new `create-vttforge` package) walks the
281
+ user through naming the project, picking system/module + TypeScript/JavaScript,
282
+ and writing a complete VTTForge starter into a new directory.
283
+
284
+ **Bin commands:**
285
+ - `vttforge init [name]` — interactive scaffolder. Honors `--type`, `--lang`,
286
+ `--no-install`, `--no-git` flags; everything else is prompted for via
287
+ `@clack/prompts`. Detects the calling package manager via
288
+ `npm_config_user_agent` (pnpm / npm / bun / yarn) and offers to run
289
+ `<pm> install` after writing files. Optionally initializes a git repo
290
+ with an initial commit.
291
+ - `vttforge dev`, `vttforge build`, `vttforge audit` — reserved subcommands
292
+ that print a discoverable next-step message until their real
293
+ implementations land in a later release.
294
+
295
+ **Four built-in templates** under `packages/cli/templates/`:
296
+ - `system-ts` / `system-js` — realistic Foundry v13+ system that mirrors
297
+ the reference `examples/simple-system`. Ships `CharacterData` + `GearData`
298
+ TypeDataModels, `CharacterSheet` + `GearSheet` extending `BaseActorSheet`
299
+ / `BaseItemSheet`, a `createMigrationRunner` with one example migration,
300
+ Handlebars templates for both sheets, the four v13 manifest shapes
301
+ (`grid`, `styles: [{src}]`, `flags.hotReload` at the root of `flags`,
302
+ per-subtype `htmlFields`), and a tagged-release GitHub Actions workflow
303
+ that builds + zips + publishes to a GitHub Release.
304
+ - `module-ts` / `module-js` — minimal but realistic module. Registers a
305
+ client-scope setting via `SystemConfig` from `@vttforge/core`, listens on
306
+ `renderActorSheetV2` to tag the rendered sheet with a discreet badge,
307
+ exposes a tiny API on `game.modules.get(id).api`, and ships the same
308
+ tag-driven release workflow.
309
+
310
+ **New package — `create-vttforge`** — thin wrapper around `@vttforge/cli init`
311
+ that enables the `pnpm create vttforge my-system` / `npm create vttforge@latest my-system`
312
+ UX familiar from the `create-vite` / `create-next-app` ecosystem.
313
+
314
+ 44 unit tests cover the substitution logic, the package-manager detector, and
315
+ a per-template integration suite that scaffolds every variant into a temporary
316
+ directory and asserts shape (no leftover placeholders, valid manifest,
317
+ expected files). Templates are excluded from biome and vitest scanning since
318
+ they contain `{{PLACEHOLDER}}` strings that aren't valid TypeScript/JSON until
319
+ substitution.
320
+
321
+ - 4d34985: Move to Vite 8.
322
+
323
+ The plugin's `vite` peer range now reads `^8.2.2`, so a project still on
324
+ Vite 6 or 7 will not satisfy it. The four `vttforge init` templates move
325
+ with it, which keeps a freshly scaffolded project free of a peer conflict.
326
+
327
+ The plugin itself needed no code change — it uses only the `config` hook
328
+ and the `Plugin` and `UserConfig` types, all unchanged across the two
329
+ majors.
330
+
331
+ ### Patch Changes
332
+
333
+ - 6e36de2: Move the release-zip writer to archiver 8.
334
+
335
+ Archiver 8 is ESM-only and dropped its default export, so the old
336
+ `archiver('zip', options)` factory call is gone. The writer now builds the
337
+ named `ZipArchive` class directly. Same options, same output: package
338
+ contents sit at the zip root with no wrapper folder, and LICENSE, README
339
+ and CHANGELOG still ride along from the project root when present.
340
+
341
+ - edd88fe: Move the prompts to `@clack/prompts` 1.x, and fix a validator that let a
342
+ missing package id through.
343
+
344
+ Clack 1.x hands validators `string | undefined` instead of `string`, since
345
+ it calls them before anything is typed. Widening the signatures surfaced a
346
+ latent bug in the package-id check: it called `RegExp.test` on the raw
347
+ value, and an undefined value coerces to the string `"undefined"`, which
348
+ matches the allowed pattern. A missing id therefore passed validation. The
349
+ check now coalesces first.
350
+
351
+ The three validators are exported and covered by tests. They were only
352
+ reachable through the interactive prompt before, so nothing exercised them.
353
+
354
+ - 4ba725c: Point the scaffolding templates at the versions this release publishes.
355
+
356
+ The templates asked for `@vttforge/core@^0.3.0` and
357
+ `@vttforge/vite-plugin@^0.1.0`. On a 0.x version a caret pins the minor, so
358
+ `^0.3.0` means `>=0.3.0 <0.4.0` — and this release takes core to 0.4.0 and
359
+ the plugin to 0.2.0. Left alone, every project scaffolded after the release
360
+ would ask for versions the release had just superseded.
361
+
362
+ ## 0.0.1
363
+
364
+ Initial placeholder release — package name reserved on npm. Implementation lands in v0.3.0.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fabricio Cavalcante de Souza and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,11 +1,39 @@
1
1
  # @vttforge/cli
2
2
 
3
- Command-line tool to scaffold and build FoundryVTT systems and modules.
3
+ VTTForge CLI scaffolding, dev loop, and release builds for Foundry VTT v13+ systems and modules.
4
4
 
5
- > 🚧 **Placeholder release.** This is a reserved name on the npm registry. The first usable version is coming soon.
5
+ ## Commands
6
6
 
7
- Track progress and contribute at **https://github.com/vttforge/vttforge**.
7
+ ```bash
8
+ vttforge init <name> [--type system|module] [--lang ts|js] [--no-install] [--no-git]
9
+ vttforge dev [--foundry-data <path>]
10
+ vttforge build
11
+ ```
8
12
 
9
- ---
13
+ - `vttforge init` — interactive scaffolder. Asks for package id, title, author, license, Foundry compatibility version, and writes a runnable system/module starter into `<name>/`. Detects the calling package manager (`pnpm`, `npm`, `bun`, `yarn`) via `npm_config_user_agent` and offers to install deps + `git init`. Equivalent UX from `pnpm create vttforge <name>` via the `create-vttforge` package.
14
+ - `vttforge dev` — one-shot `vite build`, then symlink `dist/` into your Foundry user-data directory under `Data/<systems|modules>/<id>/`, then `vite build --watch` with inherited stdio. Cleans up the symlink on `Ctrl-C`. First run prompts for the Foundry data dir and saves the choice to `.vttforge/config.json`; subsequent runs skip the prompt. Override with `--foundry-data <path>` (alias: `--data-dir`) or set `FOUNDRY_DATA_DIR` in your environment.
15
+ - `vttforge build` — production `vite build` followed by `<id>-<version>.zip` emission at the project root. Pulls `LICENSE`, `README.md`, and `CHANGELOG.md` from the project root into the zip when present.
10
16
 
11
- VTTForge is an independent, community-developed project. It is not affiliated with, endorsed by, or sponsored by Foundry Gaming LLC. "Foundry Virtual Tabletop", "Foundry VTT", and "FVTT" are trademarks of Foundry Gaming LLC.
17
+ ## Library exports
18
+
19
+ `@vttforge/cli` also exposes its internals for consumers building higher-level tooling:
20
+
21
+ ```ts
22
+ import {
23
+ runInit, runDev, runBuild,
24
+ scaffold, substitute, templatesRoot,
25
+ resolveFoundryDataDir, foundryPackagesDir,
26
+ readManifest, emitZip,
27
+ createLink, removeLink, readLinkTarget,
28
+ resolveViteInvocation, runViteBuildOnce, spawnViteWatch,
29
+ detectPackageManager, detectProjectPackageManager, execInvocation, installCommand,
30
+ } from '@vttforge/cli';
31
+ ```
32
+
33
+ ## Foundry-aware HMR
34
+
35
+ Run Foundry with `--hotReload` (e.g. `foundryvtt --hotReload`) and `vttforge dev` becomes a live-reload loop without further setup: Foundry's built-in chokidar watcher follows the symlink, and its dispatcher swaps CSS / Handlebars / JSON files in place when vite re-emits them.
36
+
37
+ ## Stack
38
+
39
+ Citty (commands) + Clack (prompts) + Archiver (zip).
package/dist/bin.d.mts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/bin.mjs ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ import { M as runAuditCommand, c as runDev, n as ScaffoldError, r as runInit, t as VTTFORGE_CLI_VERSION, x as runBuild } from "./src-CWhUNM__.mjs";
3
+ import { defineCommand, runMain } from "citty";
4
+ //#region src/cli.ts
5
+ /**
6
+ * vttforge command tree.
7
+ *
8
+ * Kept apart from `bin.ts`, which only runs it. Argument definitions are the
9
+ * CLI's contract with its users — `--no-install` skipping the install, an
10
+ * alias resolving to its canonical name — and a module that calls `runMain`
11
+ * on import cannot be reached by a test. Exporting the commands from here
12
+ * lets the suite parse real argv against the real definitions.
13
+ */
14
+ const init = defineCommand({
15
+ meta: {
16
+ name: "init",
17
+ description: "Scaffold a new Foundry VTT system or module from a VTTForge template"
18
+ },
19
+ args: {
20
+ name: {
21
+ type: "positional",
22
+ description: "Directory name for the new system/module (also the default manifest id)",
23
+ required: false
24
+ },
25
+ type: {
26
+ type: "string",
27
+ description: "Package type: system | module"
28
+ },
29
+ lang: {
30
+ type: "string",
31
+ description: "Language: ts | js"
32
+ },
33
+ id: {
34
+ type: "string",
35
+ description: "Manifest id (defaults to a slug of the directory name)"
36
+ },
37
+ title: {
38
+ type: "string",
39
+ description: "Human-readable title shown in Foundry setup screens"
40
+ },
41
+ description: {
42
+ type: "string",
43
+ description: "One-line description for the manifest"
44
+ },
45
+ author: {
46
+ type: "string",
47
+ description: "Author name for the manifest (defaults to the git author)"
48
+ },
49
+ license: {
50
+ type: "string",
51
+ description: "SPDX license id (default MIT)"
52
+ },
53
+ yes: {
54
+ type: "boolean",
55
+ alias: "y",
56
+ default: false,
57
+ description: "Accept defaults for anything not passed, without prompting"
58
+ },
59
+ install: {
60
+ type: "boolean",
61
+ default: true,
62
+ description: "Install dependencies after scaffold (use --no-install to skip)"
63
+ },
64
+ git: {
65
+ type: "boolean",
66
+ default: true,
67
+ description: "Initialize a git repository after scaffold (use --no-git to skip)"
68
+ }
69
+ },
70
+ async run({ args }) {
71
+ try {
72
+ await runInit({
73
+ name: typeof args.name === "string" ? args.name : void 0,
74
+ type: typeof args.type === "string" ? args.type : void 0,
75
+ lang: typeof args.lang === "string" ? args.lang : void 0,
76
+ id: typeof args.id === "string" ? args.id : void 0,
77
+ title: typeof args.title === "string" ? args.title : void 0,
78
+ description: typeof args.description === "string" ? args.description : void 0,
79
+ author: typeof args.author === "string" ? args.author : void 0,
80
+ license: typeof args.license === "string" ? args.license : void 0,
81
+ yes: args.yes === true,
82
+ noInstall: args.install === false,
83
+ noGit: args.git === false
84
+ });
85
+ } catch (err) {
86
+ if (!(err instanceof ScaffoldError)) console.error(err instanceof Error ? err.message : String(err));
87
+ process.exit(1);
88
+ }
89
+ }
90
+ });
91
+ const dev = defineCommand({
92
+ meta: {
93
+ name: "dev",
94
+ description: "Symlink dist/ into Foundry Data and run vite build --watch"
95
+ },
96
+ args: {
97
+ "foundry-data": {
98
+ type: "string",
99
+ alias: "data-dir",
100
+ description: "Override the Foundry user-data directory (skips env / config / first-run prompt)"
101
+ },
102
+ "hmr-port": {
103
+ type: "string",
104
+ description: "Port for the hot reload bridge (default 31313)"
105
+ }
106
+ },
107
+ async run({ args }) {
108
+ const explicit = (typeof args["foundry-data"] === "string" ? args["foundry-data"] : void 0) ?? (typeof args["data-dir"] === "string" ? args["data-dir"] : void 0);
109
+ const rawPort = typeof args["hmr-port"] === "string" ? args["hmr-port"] : void 0;
110
+ const hmrPort = rawPort === void 0 ? void 0 : Number(rawPort);
111
+ if (hmrPort !== void 0 && !Number.isInteger(hmrPort)) {
112
+ console.error(`--hmr-port expects a whole number, got \`${rawPort}\`.`);
113
+ process.exit(1);
114
+ }
115
+ try {
116
+ await runDev({
117
+ dataDir: explicit,
118
+ hmrPort
119
+ });
120
+ } catch (err) {
121
+ console.error(err instanceof Error ? err.message : String(err));
122
+ process.exit(1);
123
+ }
124
+ }
125
+ });
126
+ const build = defineCommand({
127
+ meta: {
128
+ name: "build",
129
+ description: "Run vite build (production) and emit <id>-<version>.zip for foundryvtt.com"
130
+ },
131
+ async run() {
132
+ try {
133
+ await runBuild();
134
+ } catch (err) {
135
+ console.error(err instanceof Error ? err.message : String(err));
136
+ process.exit(1);
137
+ }
138
+ }
139
+ });
140
+ const audit = defineCommand({
141
+ meta: {
142
+ name: "audit",
143
+ description: "Scan a system/module project for the seven v13 manifest + code footguns"
144
+ },
145
+ args: {
146
+ path: {
147
+ type: "positional",
148
+ description: "Project root to scan (defaults to the current directory)",
149
+ required: false
150
+ },
151
+ json: {
152
+ type: "boolean",
153
+ default: false,
154
+ description: "Emit a machine-readable JSON report instead of markdown"
155
+ },
156
+ strict: {
157
+ type: "boolean",
158
+ default: false,
159
+ description: "Exit non-zero on any finding (default: only HIGH triggers a non-zero exit)"
160
+ }
161
+ },
162
+ async run({ args }) {
163
+ try {
164
+ const result = await runAuditCommand({
165
+ cwd: typeof args.path === "string" ? args.path : void 0,
166
+ format: args.json === true ? "json" : "markdown",
167
+ strict: args.strict === true
168
+ });
169
+ if (result.exitCode !== 0) process.exitCode = result.exitCode;
170
+ } catch (err) {
171
+ console.error(err instanceof Error ? err.message : String(err));
172
+ process.exitCode = 1;
173
+ }
174
+ }
175
+ });
176
+ const main = defineCommand({
177
+ meta: {
178
+ name: "vttforge",
179
+ version: VTTFORGE_CLI_VERSION,
180
+ description: "VTTForge CLI — scaffold, dev, build for Foundry v13+ systems and modules"
181
+ },
182
+ subCommands: {
183
+ init,
184
+ dev,
185
+ build,
186
+ audit
187
+ }
188
+ });
189
+ //#endregion
190
+ //#region src/bin.ts
191
+ /**
192
+ * vttforge bin — the executable entry.
193
+ *
194
+ * Nothing but the launch. The command tree lives in `cli.ts` so tests can
195
+ * import it without running the CLI.
196
+ */
197
+ runMain(main);
198
+ //#endregion
199
+ export {};
200
+
201
+ //# sourceMappingURL=bin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.mjs","names":[],"sources":["../src/cli.ts","../src/bin.ts"],"sourcesContent":["/**\n * vttforge command tree.\n *\n * Kept apart from `bin.ts`, which only runs it. Argument definitions are the\n * CLI's contract with its users — `--no-install` skipping the install, an\n * alias resolving to its canonical name — and a module that calls `runMain`\n * on import cannot be reached by a test. Exporting the commands from here\n * lets the suite parse real argv against the real definitions.\n */\nimport { defineCommand } from 'citty';\nimport { runAuditCommand } from './commands/audit.js';\nimport { runBuild } from './commands/build.js';\nimport { runDev } from './commands/dev.js';\nimport { runInit, ScaffoldError } from './commands/init.js';\nimport { VTTFORGE_CLI_VERSION } from './index.js';\n\nexport const init = defineCommand({\n meta: {\n name: 'init',\n description: 'Scaffold a new Foundry VTT system or module from a VTTForge template',\n },\n args: {\n name: {\n type: 'positional',\n description: 'Directory name for the new system/module (also the default manifest id)',\n required: false,\n },\n type: {\n type: 'string',\n description: 'Package type: system | module',\n },\n lang: {\n type: 'string',\n description: 'Language: ts | js',\n },\n id: {\n type: 'string',\n description: 'Manifest id (defaults to a slug of the directory name)',\n },\n title: {\n type: 'string',\n description: 'Human-readable title shown in Foundry setup screens',\n },\n description: {\n type: 'string',\n description: 'One-line description for the manifest',\n },\n author: {\n type: 'string',\n description: 'Author name for the manifest (defaults to the git author)',\n },\n license: {\n type: 'string',\n description: 'SPDX license id (default MIT)',\n },\n // Without a terminal there is nothing for a prompt to read, so it waits\n // forever. This flag takes the defaults instead — and the scaffolder\n // also assumes it when stdin is not a TTY, so CI need not pass it.\n yes: {\n type: 'boolean',\n alias: 'y',\n default: false,\n description: 'Accept defaults for anything not passed, without prompting',\n },\n // Citty parses `--no-X` as `args.X === false`, so define affirmative\n // flags with a true default. `--no-install` then yields `install: false`\n // which we forward as `noInstall: true`.\n install: {\n type: 'boolean',\n default: true,\n description: 'Install dependencies after scaffold (use --no-install to skip)',\n },\n git: {\n type: 'boolean',\n default: true,\n description: 'Initialize a git repository after scaffold (use --no-git to skip)',\n },\n },\n async run({ args }) {\n try {\n await runInit({\n name: typeof args.name === 'string' ? args.name : undefined,\n type: typeof args.type === 'string' ? (args.type as 'system' | 'module') : undefined,\n lang: typeof args.lang === 'string' ? (args.lang as 'ts' | 'js') : undefined,\n id: typeof args.id === 'string' ? args.id : undefined,\n title: typeof args.title === 'string' ? args.title : undefined,\n description: typeof args.description === 'string' ? args.description : undefined,\n author: typeof args.author === 'string' ? args.author : undefined,\n license: typeof args.license === 'string' ? args.license : undefined,\n yes: args.yes === true,\n noInstall: args.install === false,\n noGit: args.git === false,\n });\n } catch (err) {\n // ScaffoldError already surfaced its message via `p.cancel`; anything\n // else is a real crash worth showing. Either way the bin exits 1.\n if (!(err instanceof ScaffoldError)) {\n console.error(err instanceof Error ? err.message : String(err));\n }\n process.exit(1);\n }\n },\n});\n\nexport const dev = defineCommand({\n meta: {\n name: 'dev',\n description: 'Symlink dist/ into Foundry Data and run vite build --watch',\n },\n args: {\n 'foundry-data': {\n type: 'string',\n alias: 'data-dir',\n description:\n 'Override the Foundry user-data directory (skips env / config / first-run prompt)',\n },\n 'hmr-port': {\n type: 'string',\n description: 'Port for the hot reload bridge (default 31313)',\n },\n },\n async run({ args }) {\n // Citty surfaces aliases under the canonical name. Belt-and-suspenders:\n // accept either spelling so older docs and muscle memory keep working.\n const explicit =\n (typeof args['foundry-data'] === 'string' ? args['foundry-data'] : undefined) ??\n (typeof args['data-dir'] === 'string' ? args['data-dir'] : undefined);\n // A non-numeric port is the user's typo, not a reason to fall back to a\n // port they did not ask for — say so and stop.\n const rawPort = typeof args['hmr-port'] === 'string' ? args['hmr-port'] : undefined;\n const hmrPort = rawPort === undefined ? undefined : Number(rawPort);\n if (hmrPort !== undefined && !Number.isInteger(hmrPort)) {\n console.error(`--hmr-port expects a whole number, got \\`${rawPort}\\`.`);\n process.exit(1);\n }\n try {\n await runDev({ dataDir: explicit, hmrPort });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\nexport const build = defineCommand({\n meta: {\n name: 'build',\n description: 'Run vite build (production) and emit <id>-<version>.zip for foundryvtt.com',\n },\n async run() {\n try {\n await runBuild();\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\nexport const audit = defineCommand({\n meta: {\n name: 'audit',\n description: 'Scan a system/module project for the seven v13 manifest + code footguns',\n },\n args: {\n path: {\n type: 'positional',\n description: 'Project root to scan (defaults to the current directory)',\n required: false,\n },\n json: {\n type: 'boolean',\n default: false,\n description: 'Emit a machine-readable JSON report instead of markdown',\n },\n strict: {\n type: 'boolean',\n default: false,\n description: 'Exit non-zero on any finding (default: only HIGH triggers a non-zero exit)',\n },\n },\n async run({ args }) {\n try {\n const result = await runAuditCommand({\n cwd: typeof args.path === 'string' ? args.path : undefined,\n format: args.json === true ? 'json' : 'markdown',\n strict: args.strict === true,\n });\n // Set exitCode instead of calling process.exit so any pending stdout\n // writes (the JSON / markdown report) get flushed before the process\n // tears down. process.exit(1) would truncate large reports on the\n // exact runs CI needs them intact.\n if (result.exitCode !== 0) process.exitCode = result.exitCode;\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n }\n },\n});\n\nexport const main = defineCommand({\n meta: {\n name: 'vttforge',\n version: VTTFORGE_CLI_VERSION,\n description: 'VTTForge CLI — scaffold, dev, build for Foundry v13+ systems and modules',\n },\n subCommands: {\n init,\n dev,\n build,\n audit,\n },\n});\n","#!/usr/bin/env node\n/**\n * vttforge bin — the executable entry.\n *\n * Nothing but the launch. The command tree lives in `cli.ts` so tests can\n * import it without running the CLI.\n */\nimport { runMain } from 'citty';\nimport { main } from './cli.js';\n\nrunMain(main);\n"],"mappings":"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"}