@vttforge/cli 0.0.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +350 -0
- package/LICENSE +21 -0
- package/README.md +33 -5
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +201 -0
- package/dist/bin.mjs.map +1 -0
- package/dist/index.d.mts +496 -0
- package/dist/index.mjs +2 -0
- package/dist/src-CWhUNM__.mjs +2443 -0
- package/dist/src-CWhUNM__.mjs.map +1 -0
- package/package.json +62 -9
- package/templates/module-js/.github/workflows/release.yml +92 -0
- package/templates/module-js/README.md +57 -0
- package/templates/module-js/_gitignore +12 -0
- package/templates/module-js/lang/en.json +11 -0
- package/templates/module-js/module.json +20 -0
- package/templates/module-js/package.json +24 -0
- package/templates/module-js/scripts/main.mjs +65 -0
- package/templates/module-js/styles/main.css +16 -0
- package/templates/module-js/vite.config.mjs +13 -0
- package/templates/module-ts/.github/workflows/release.yml +92 -0
- package/templates/module-ts/README.md +57 -0
- package/templates/module-ts/_gitignore +12 -0
- package/templates/module-ts/lang/en.json +11 -0
- package/templates/module-ts/module.json +20 -0
- package/templates/module-ts/package.json +26 -0
- package/templates/module-ts/scripts/foundry-globals.ts +23 -0
- package/templates/module-ts/scripts/main.ts +71 -0
- package/templates/module-ts/styles/main.css +16 -0
- package/templates/module-ts/tsconfig.json +18 -0
- package/templates/module-ts/vite.config.mjs +13 -0
- package/templates/system-js/.github/workflows/release.yml +92 -0
- package/templates/system-js/README.md +57 -0
- package/templates/system-js/_gitignore +12 -0
- package/templates/system-js/lang/en.json +55 -0
- package/templates/system-js/package.json +25 -0
- package/templates/system-js/scripts/data/character-data.mjs +79 -0
- package/templates/system-js/scripts/data/gear-data.mjs +37 -0
- package/templates/system-js/scripts/main.mjs +79 -0
- package/templates/system-js/scripts/migrations.mjs +43 -0
- package/templates/system-js/scripts/sheets/character-sheet.mjs +164 -0
- package/templates/system-js/scripts/sheets/gear-sheet.mjs +65 -0
- package/templates/system-js/styles/main.css +495 -0
- package/templates/system-js/system.json +44 -0
- package/templates/system-js/template.json +8 -0
- package/templates/system-js/templates/actor/character-sheet.hbs +168 -0
- package/templates/system-js/templates/item/gear-sheet.hbs +54 -0
- package/templates/system-js/vite.config.mjs +16 -0
- package/templates/system-ts/.github/workflows/release.yml +92 -0
- package/templates/system-ts/README.md +57 -0
- package/templates/system-ts/_gitignore +12 -0
- package/templates/system-ts/lang/en.json +55 -0
- package/templates/system-ts/package.json +27 -0
- package/templates/system-ts/scripts/data/character-data.ts +81 -0
- package/templates/system-ts/scripts/data/gear-data.ts +37 -0
- package/templates/system-ts/scripts/foundry-globals.ts +33 -0
- package/templates/system-ts/scripts/main.ts +82 -0
- package/templates/system-ts/scripts/migrations.ts +43 -0
- package/templates/system-ts/scripts/sheets/character-sheet.ts +202 -0
- package/templates/system-ts/scripts/sheets/gear-sheet.ts +68 -0
- package/templates/system-ts/styles/main.css +495 -0
- package/templates/system-ts/system.json +44 -0
- package/templates/system-ts/template.json +8 -0
- package/templates/system-ts/templates/actor/character-sheet.hbs +168 -0
- package/templates/system-ts/templates/item/gear-sheet.hbs +54 -0
- package/templates/system-ts/tsconfig.json +18 -0
- package/templates/system-ts/vite.config.mjs +16 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# @vttforge/cli
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3eb8c96: `vttforge init` can now run without a terminal.
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
vttforge init pdf-character-sheet --type module --lang ts \
|
|
15
|
+
--title "PDF Character Sheet" --license Apache-2.0 --yes
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The directory name is the one thing with no sensible default. Without a terminal to ask, the run stops and says so.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 11187a0: Say what the scaffolded schemas mean.
|
|
23
|
+
|
|
24
|
+
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`.
|
|
25
|
+
|
|
26
|
+
The six ability scores share one spec instead of repeating it six times.
|
|
27
|
+
- 353633c: Build each ability score from a factory rather than one shared options object.
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
- c24b2e9: Point the templates at the core version this release publishes.
|
|
31
|
+
|
|
32
|
+
The `ColorField` and presence fixes take `@vttforge/core` to 0.6.0, and a
|
|
33
|
+
caret pins the minor on a 0.x version, so the templates' `^0.5.0` would not
|
|
34
|
+
have matched.
|
|
35
|
+
|
|
36
|
+
## 0.4.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- 17a6b8c: Redraw only the sheets a template change actually affects.
|
|
41
|
+
|
|
42
|
+
A sheet declares its templates in `static PARTS`, so the mapping is on the
|
|
43
|
+
open window itself — no build-time graph needed. Each part names a primary
|
|
44
|
+
`template` and may list further `templates`, which is where a partial it
|
|
45
|
+
pulls in appears. Only the affected parts re-render, so scroll position and
|
|
46
|
+
focus survive everywhere else. When nothing claims the file the whole screen
|
|
47
|
+
still redraws, because a partial can be reached without any part naming it.
|
|
48
|
+
|
|
49
|
+
The watcher now also skips a file whose content did not change. Vite rewrites
|
|
50
|
+
its entire output on every build, so a filesystem event alone means "the
|
|
51
|
+
bundler ran", not "the developer edited this" — and without this the language
|
|
52
|
+
file gets resent untouched on every save, and the JSON path redraws every open
|
|
53
|
+
window, undoing the scoping on the file that did change.
|
|
54
|
+
|
|
55
|
+
Measured in a running world with 21 applications open: editing one sheet
|
|
56
|
+
template went from 35 renders to 1.
|
|
57
|
+
|
|
58
|
+
## 0.3.0
|
|
59
|
+
|
|
60
|
+
### Minor Changes
|
|
61
|
+
|
|
62
|
+
- 5b72b4b: `vttforge dev` now applies saves in place, without a page refresh.
|
|
63
|
+
|
|
64
|
+
It opens a small WebSocket, links `@vttforge/dev-module` into Foundry's
|
|
65
|
+
modules directory, and watches the build output — sending a payload per
|
|
66
|
+
changed CSS, template or language file.
|
|
67
|
+
|
|
68
|
+
The socket is hand-written rather than pulled from a package. The CLI ships
|
|
69
|
+
to every consumer, so each dependency is one they install too, and what is
|
|
70
|
+
needed here is a narrow slice of the protocol: accept the upgrade, send
|
|
71
|
+
unmasked text frames, notice when a client leaves.
|
|
72
|
+
|
|
73
|
+
Watching `dist/` rather than hooking into Vite keeps this correct across
|
|
74
|
+
bundler versions, and gives the served path directly — `dist/` is what
|
|
75
|
+
Foundry mounts.
|
|
76
|
+
|
|
77
|
+
Failing to start the bridge costs hot reload, not the dev loop. A busy port
|
|
78
|
+
or a missing companion package prints why and carries on watching.
|
|
79
|
+
|
|
80
|
+
New: `--hmr-port` to move the bridge off 31313.
|
|
81
|
+
|
|
82
|
+
### Patch Changes
|
|
83
|
+
|
|
84
|
+
- 1fb967d: Close the three soundness gaps `vttforge audit` shipped with, and a fourth
|
|
85
|
+
found while fixing them.
|
|
86
|
+
|
|
87
|
+
- `filePathFields` is an object whose keys are the field paths, not a flat
|
|
88
|
+
array. Reading it as an array dropped every correctly declared path, so
|
|
89
|
+
rule 004 reported declared fields as missing.
|
|
90
|
+
- Modules register subtypes as `<moduleId>.<type>` while the manifest
|
|
91
|
+
declares the bare key. The two were compared verbatim, so no module
|
|
92
|
+
registration ever matched its own declaration.
|
|
93
|
+
- Rule 007 searched every schema in the project for the token attribute.
|
|
94
|
+
Token bars read `actor.system`, so an Item model declaring the same path
|
|
95
|
+
satisfied the check on a system whose bars were in fact broken. The
|
|
96
|
+
narrowing applies only when Actor registrations are known; with none
|
|
97
|
+
found the rule falls back to the previous behaviour.
|
|
98
|
+
- Registrations through bracket notation were invisible to the scanner
|
|
99
|
+
entirely — which is the only form modules can use, since a dot in the key
|
|
100
|
+
rules out property access.
|
|
101
|
+
- c90357e: Point the templates at the CLI version this release publishes.
|
|
102
|
+
|
|
103
|
+
The templates asked for `@vttforge/cli@^0.2.0` while the release takes it to
|
|
104
|
+
0.3.0. A caret pins the minor on a 0.x version, so that range would not have
|
|
105
|
+
matched — every project scaffolded after the release would ask for a version
|
|
106
|
+
the release had just superseded.
|
|
107
|
+
|
|
108
|
+
## 0.2.0
|
|
109
|
+
|
|
110
|
+
### Minor Changes
|
|
111
|
+
|
|
112
|
+
- 9462144: Require Node 26.
|
|
113
|
+
|
|
114
|
+
The floor moves from `>=22.14.0` to `>=26.0.0` across every package and the
|
|
115
|
+
four scaffolding templates, and the bundler target for the Node-side
|
|
116
|
+
packages moves from `node22` to `node26`.
|
|
117
|
+
|
|
118
|
+
Node 22 entered maintenance in October 2025 and receives security fixes
|
|
119
|
+
only. Node 26 becomes the active LTS line on 2026-10-28.
|
|
120
|
+
|
|
121
|
+
This is breaking for anyone on Node 22 or 24. It is marked `minor` rather
|
|
122
|
+
than `major` on purpose: these packages are still on 0.x, where a minor
|
|
123
|
+
signals the break, and a major would push every package to 1.0.0 — a claim
|
|
124
|
+
of API stability that has not been audited, on packages two of which are
|
|
125
|
+
still stubs.
|
|
126
|
+
|
|
127
|
+
The templates move to the versions this release publishes. On 0.x a caret
|
|
128
|
+
pins the minor, so their old ranges would not have matched.
|
|
129
|
+
|
|
130
|
+
CI now pins Node through `actions/setup-node` instead of inheriting whatever
|
|
131
|
+
the runner image ships, so the version the packages declare is the version
|
|
132
|
+
they are tested on. It was not before: the workflow took the image's Node,
|
|
133
|
+
and nothing enforced the declared floor because `engine-strict` is not set.
|
|
134
|
+
|
|
135
|
+
## 0.1.1
|
|
136
|
+
|
|
137
|
+
### Patch Changes
|
|
138
|
+
|
|
139
|
+
- 5f03da9: Move to citty 0.2 and cover argument parsing with tests.
|
|
140
|
+
|
|
141
|
+
citty 0.2 is ESM-only, drops its runtime dependencies, and rewrites the
|
|
142
|
+
parser on top of `node:util.parseArgs`. Parsing behaviour is unchanged for
|
|
143
|
+
every flag this CLI defines — negation, aliases, positionals, `--flag=value`
|
|
144
|
+
and boolean defaults all produce identical results on 0.1.6 and 0.2.2.
|
|
145
|
+
|
|
146
|
+
The command tree moves from `bin.ts` to `cli.ts`, leaving the bin as the
|
|
147
|
+
launch only. The definitions were unreachable from tests before, because a
|
|
148
|
+
module that calls `runMain` on import runs the CLI. They are now covered,
|
|
149
|
+
including `--no-install` and `--no-git`, which would have made the
|
|
150
|
+
scaffolder install dependencies for someone who asked it not to had the
|
|
151
|
+
parser rewrite changed them.
|
|
152
|
+
|
|
153
|
+
## 0.1.0
|
|
154
|
+
|
|
155
|
+
### Minor Changes
|
|
156
|
+
|
|
157
|
+
- 139802c: feat(cli): ship `vttforge audit` — scan for seven v13 footguns
|
|
158
|
+
|
|
159
|
+
Closes the third and final slice of Track 1. `vttforge audit [path]` scans
|
|
160
|
+
a system or module project root and reports findings against the
|
|
161
|
+
`VTTF-AUDIT-NNN` catalog of v13 manifest + code footguns.
|
|
162
|
+
|
|
163
|
+
**Rules implemented:**
|
|
164
|
+
|
|
165
|
+
| ID | Severity | Scope | What it catches |
|
|
166
|
+
| ---------------- | -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
167
|
+
| `VTTF-AUDIT-001` | HIGH | manifest | `flags.hotReload` as array (v12) or missing required `extensions` key. Silently disables HMR. |
|
|
168
|
+
| `VTTF-AUDIT-002` | MEDIUM | manifest | Top-level `gridDistance` / `gridUnits` (v12) instead of `grid: {type, distance, units, diagonals}` (v13). Auto-migrates today, removed in v14. |
|
|
169
|
+
| `VTTF-AUDIT-003` | LOW | manifest | `styles: ["foo.css"]` (string array) instead of `[{src, layer?}]` (v13). Auto-migrates but loses cascade-layer control. |
|
|
170
|
+
| `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. |
|
|
171
|
+
| `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. |
|
|
172
|
+
| `VTTF-AUDIT-006` | LOW | source | `_addDataFieldMigrations()` override on a TypeDataModel subclass. Real API is `static migrateData(source)` calling singular `super._addDataFieldMigration(...)`. |
|
|
173
|
+
| `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. |
|
|
174
|
+
|
|
175
|
+
**CLI surface:**
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
vttforge audit # scan cwd, print markdown
|
|
179
|
+
vttforge audit ./my-project # scan a different path
|
|
180
|
+
vttforge audit --json # machine-readable JSON for CI piping
|
|
181
|
+
vttforge audit --strict # exit 1 on any finding (default: HIGH only)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Exit codes:**
|
|
185
|
+
- `0` — clean, or only MEDIUM/LOW findings (advisory mode)
|
|
186
|
+
- `1` — at least one HIGH finding, or any finding in `--strict`
|
|
187
|
+
|
|
188
|
+
Uses `process.exitCode` (not `process.exit`) so piped JSON reports aren't
|
|
189
|
+
truncated on failing CI runs.
|
|
190
|
+
|
|
191
|
+
**Implementation notes:**
|
|
192
|
+
- Pure read-only — never modifies the source tree.
|
|
193
|
+
- Regex-based source scanning. The trade-off is occasional false negatives
|
|
194
|
+
on heavily-formatted code; an AST dependency (TypeScript compiler) would
|
|
195
|
+
add ~30MB to the CLI for seven pattern checks. Rule 007's
|
|
196
|
+
`SchemaField({value, max})` detection uses a balanced-brace scan plus a
|
|
197
|
+
depth-tracking top-level-key extractor to correctly distinguish
|
|
198
|
+
SchemaField siblings from nested constructor options.
|
|
199
|
+
- Rule 005 distinguishes `extends TypeDataModel` (direct, flagged) from
|
|
200
|
+
`extends BaseTypeDataModel()` (VTTForge factory, safe) via the `\b`
|
|
201
|
+
word boundary, and is checked per class so a sibling subclass without
|
|
202
|
+
the hook doesn't hide behind a sibling that has it.
|
|
203
|
+
- Rule 004 takes the FULL schema path through enclosing SchemaField
|
|
204
|
+
wrappers and matches exactly against declared paths (with `system.`
|
|
205
|
+
prefix stripped — Foundry convention).
|
|
206
|
+
|
|
207
|
+
**Tests:** 247 passing (+13 new test files, ~110 new test cases). Integration
|
|
208
|
+
suite scaffolds each of the four templates and runs the audit against the
|
|
209
|
+
output — they must report zero findings, otherwise either a template
|
|
210
|
+
regressed or the audit grew a false positive.
|
|
211
|
+
|
|
212
|
+
- 09de435: feat(cli): ship `vttforge dev` and `vttforge build`
|
|
213
|
+
|
|
214
|
+
`@vttforge/cli` graduates from "scaffold-only" to a full local dev loop.
|
|
215
|
+
|
|
216
|
+
**`vttforge dev`** — single-process dev experience for system/module work:
|
|
217
|
+
1. Runs `vite build` once to populate `dist/` and emit the manifest.
|
|
218
|
+
2. Reads the manifest, detects whether the project is a system or module.
|
|
219
|
+
3. Resolves the Foundry user-data directory via a four-step chain:
|
|
220
|
+
`--data-dir` flag → `FOUNDRY_DATA_DIR` env → project's
|
|
221
|
+
`.vttforge/config.json` → OS default with interactive first-run
|
|
222
|
+
prompt that saves the choice. OS detection honors `XDG_DATA_HOME`
|
|
223
|
+
on Linux and `%LOCALAPPDATA%` on Windows.
|
|
224
|
+
4. Drops a symlink at `<dataRoot>/Data/<systems|modules>/<id>` pointing
|
|
225
|
+
at the project's `dist/`. Cross-platform — `junction` on Windows so
|
|
226
|
+
the call succeeds without Developer Mode or admin elevation; safety
|
|
227
|
+
rails refuse to overwrite real files/directories.
|
|
228
|
+
5. Spawns `vite build --watch` with inherited stdio, blocks until
|
|
229
|
+
SIGINT/SIGTERM, then cleans up the symlink and kills vite.
|
|
230
|
+
|
|
231
|
+
When Foundry runs with `--hotReload`, file saves trigger live reload
|
|
232
|
+
without a browser refresh — chokidar follows the symlink and Foundry's
|
|
233
|
+
built-in dispatcher swaps CSS/HBS/JSON in place. No additional client
|
|
234
|
+
wiring needed in this release.
|
|
235
|
+
|
|
236
|
+
**`vttforge build`** — produces a foundryvtt.com-ready release zip:
|
|
237
|
+
1. Cleans `dist/`, runs `vite build` in production mode.
|
|
238
|
+
2. Reads the manifest for `id` + `version`.
|
|
239
|
+
3. Emits `<id>-<version>.zip` at the project root with contents at the
|
|
240
|
+
zip root (no wrapper folder), pulling in `LICENSE`, `README.md`, and
|
|
241
|
+
`CHANGELOG.md` from the project root when present and not already
|
|
242
|
+
inside `dist/`.
|
|
243
|
+
|
|
244
|
+
**Templates updated** — all four (system-ts, system-js, module-ts,
|
|
245
|
+
module-js) now ship `pnpm dev` / `pnpm build` aliased to the new
|
|
246
|
+
commands, drop the manual `ln -s` instructions, and gain
|
|
247
|
+
`@vttforge/cli` as a devDependency. The release workflow continues to
|
|
248
|
+
invoke vite directly so URL injection happens after build but before
|
|
249
|
+
zipping; it now validates tag-derived version strings and passes every
|
|
250
|
+
GitHub-supplied value via `env:` instead of inline interpolation to
|
|
251
|
+
close a command-injection vector that a malicious tag push could
|
|
252
|
+
otherwise exploit.
|
|
253
|
+
|
|
254
|
+
**Internals exposed** — `runDev`, `runBuild`, `emitReleaseZip`,
|
|
255
|
+
`setupDevSymlink`, `resolveFoundryDataDir`, `createLink`/`removeLink`,
|
|
256
|
+
`readManifest`, and `emitZip` are all importable from `@vttforge/cli`
|
|
257
|
+
for consumers building higher-level tooling.
|
|
258
|
+
|
|
259
|
+
**Dependencies** — adds `archiver` (zip emission) pinned to `^7` since
|
|
260
|
+
archiver v8 went ESM-only with renamed exports that DefinitelyTyped
|
|
261
|
+
hasn't caught up to yet.
|
|
262
|
+
|
|
263
|
+
- eb1c41d: feat(cli): ship the `vttforge init` scaffolder + four built-in templates
|
|
264
|
+
|
|
265
|
+
`@vttforge/cli` now provides a real, interactive scaffolder. `vttforge init`
|
|
266
|
+
(or `pnpm create vttforge` via the new `create-vttforge` package) walks the
|
|
267
|
+
user through naming the project, picking system/module + TypeScript/JavaScript,
|
|
268
|
+
and writing a complete VTTForge starter into a new directory.
|
|
269
|
+
|
|
270
|
+
**Bin commands:**
|
|
271
|
+
- `vttforge init [name]` — interactive scaffolder. Honors `--type`, `--lang`,
|
|
272
|
+
`--no-install`, `--no-git` flags; everything else is prompted for via
|
|
273
|
+
`@clack/prompts`. Detects the calling package manager via
|
|
274
|
+
`npm_config_user_agent` (pnpm / npm / bun / yarn) and offers to run
|
|
275
|
+
`<pm> install` after writing files. Optionally initializes a git repo
|
|
276
|
+
with an initial commit.
|
|
277
|
+
- `vttforge dev`, `vttforge build`, `vttforge audit` — reserved subcommands
|
|
278
|
+
that print a discoverable next-step message until their real
|
|
279
|
+
implementations land in a later release.
|
|
280
|
+
|
|
281
|
+
**Four built-in templates** under `packages/cli/templates/`:
|
|
282
|
+
- `system-ts` / `system-js` — realistic Foundry v13+ system that mirrors
|
|
283
|
+
the reference `examples/simple-system`. Ships `CharacterData` + `GearData`
|
|
284
|
+
TypeDataModels, `CharacterSheet` + `GearSheet` extending `BaseActorSheet`
|
|
285
|
+
/ `BaseItemSheet`, a `createMigrationRunner` with one example migration,
|
|
286
|
+
Handlebars templates for both sheets, the four v13 manifest shapes
|
|
287
|
+
(`grid`, `styles: [{src}]`, `flags.hotReload` at the root of `flags`,
|
|
288
|
+
per-subtype `htmlFields`), and a tagged-release GitHub Actions workflow
|
|
289
|
+
that builds + zips + publishes to a GitHub Release.
|
|
290
|
+
- `module-ts` / `module-js` — minimal but realistic module. Registers a
|
|
291
|
+
client-scope setting via `SystemConfig` from `@vttforge/core`, listens on
|
|
292
|
+
`renderActorSheetV2` to tag the rendered sheet with a discreet badge,
|
|
293
|
+
exposes a tiny API on `game.modules.get(id).api`, and ships the same
|
|
294
|
+
tag-driven release workflow.
|
|
295
|
+
|
|
296
|
+
**New package — `create-vttforge`** — thin wrapper around `@vttforge/cli init`
|
|
297
|
+
that enables the `pnpm create vttforge my-system` / `npm create vttforge@latest my-system`
|
|
298
|
+
UX familiar from the `create-vite` / `create-next-app` ecosystem.
|
|
299
|
+
|
|
300
|
+
44 unit tests cover the substitution logic, the package-manager detector, and
|
|
301
|
+
a per-template integration suite that scaffolds every variant into a temporary
|
|
302
|
+
directory and asserts shape (no leftover placeholders, valid manifest,
|
|
303
|
+
expected files). Templates are excluded from biome and vitest scanning since
|
|
304
|
+
they contain `{{PLACEHOLDER}}` strings that aren't valid TypeScript/JSON until
|
|
305
|
+
substitution.
|
|
306
|
+
|
|
307
|
+
- 4d34985: Move to Vite 8.
|
|
308
|
+
|
|
309
|
+
The plugin's `vite` peer range now reads `^8.2.2`, so a project still on
|
|
310
|
+
Vite 6 or 7 will not satisfy it. The four `vttforge init` templates move
|
|
311
|
+
with it, which keeps a freshly scaffolded project free of a peer conflict.
|
|
312
|
+
|
|
313
|
+
The plugin itself needed no code change — it uses only the `config` hook
|
|
314
|
+
and the `Plugin` and `UserConfig` types, all unchanged across the two
|
|
315
|
+
majors.
|
|
316
|
+
|
|
317
|
+
### Patch Changes
|
|
318
|
+
|
|
319
|
+
- 6e36de2: Move the release-zip writer to archiver 8.
|
|
320
|
+
|
|
321
|
+
Archiver 8 is ESM-only and dropped its default export, so the old
|
|
322
|
+
`archiver('zip', options)` factory call is gone. The writer now builds the
|
|
323
|
+
named `ZipArchive` class directly. Same options, same output: package
|
|
324
|
+
contents sit at the zip root with no wrapper folder, and LICENSE, README
|
|
325
|
+
and CHANGELOG still ride along from the project root when present.
|
|
326
|
+
|
|
327
|
+
- edd88fe: Move the prompts to `@clack/prompts` 1.x, and fix a validator that let a
|
|
328
|
+
missing package id through.
|
|
329
|
+
|
|
330
|
+
Clack 1.x hands validators `string | undefined` instead of `string`, since
|
|
331
|
+
it calls them before anything is typed. Widening the signatures surfaced a
|
|
332
|
+
latent bug in the package-id check: it called `RegExp.test` on the raw
|
|
333
|
+
value, and an undefined value coerces to the string `"undefined"`, which
|
|
334
|
+
matches the allowed pattern. A missing id therefore passed validation. The
|
|
335
|
+
check now coalesces first.
|
|
336
|
+
|
|
337
|
+
The three validators are exported and covered by tests. They were only
|
|
338
|
+
reachable through the interactive prompt before, so nothing exercised them.
|
|
339
|
+
|
|
340
|
+
- 4ba725c: Point the scaffolding templates at the versions this release publishes.
|
|
341
|
+
|
|
342
|
+
The templates asked for `@vttforge/core@^0.3.0` and
|
|
343
|
+
`@vttforge/vite-plugin@^0.1.0`. On a 0.x version a caret pins the minor, so
|
|
344
|
+
`^0.3.0` means `>=0.3.0 <0.4.0` — and this release takes core to 0.4.0 and
|
|
345
|
+
the plugin to 0.2.0. Left alone, every project scaffolded after the release
|
|
346
|
+
would ask for versions the release had just superseded.
|
|
347
|
+
|
|
348
|
+
## 0.0.1
|
|
349
|
+
|
|
350
|
+
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
|
-
|
|
3
|
+
VTTForge CLI — scaffolding, dev loop, and release builds for Foundry VTT v13+ systems and modules.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Commands
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
package/dist/bin.mjs.map
ADDED
|
@@ -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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"}
|