fvtt-world-cli 1.0.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/LICENSE +21 -0
- package/README.md +136 -0
- package/bin/fvtt-world-cli.js +47 -0
- package/bin/prepare-package-assets.mjs +22 -0
- package/bin/sync-installed-skill.mjs +89 -0
- package/dist/index.js +16200 -0
- package/docs/README.md +71 -0
- package/docs/architecture.md +216 -0
- package/docs/commands.md +367 -0
- package/docs/compatibility.md +26 -0
- package/docs/getting-started.md +104 -0
- package/docs/images/authorization-window.png +0 -0
- package/docs/images/icon.svg +16 -0
- package/docs/images/mark.svg +15 -0
- package/docs/images/module-settings.png +0 -0
- package/docs/protocol.md +238 -0
- package/docs/security.md +233 -0
- package/docs/skill.md +65 -0
- package/package.json +50 -0
- package/skills/foundry-world-editor/SKILL.md +156 -0
- package/skills/foundry-world-editor/SKILL.md.sha256 +1 -0
package/docs/skill.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Agent skill
|
|
2
|
+
|
|
3
|
+
fvtt-world-cli ships with `foundry-world-editor`, an installable Agent Skill — a short operating
|
|
4
|
+
manual that teaches an AI agent to drive this CLI safely: how to bring the stack up and check its
|
|
5
|
+
health, the read → preview → commit → verify loop, how to classify failures before retrying, and
|
|
6
|
+
where the hard safety boundaries are. Skills follow the open Agent Skills standard, so the same
|
|
7
|
+
file works in Claude Code, Codex, and every other agent that reads `SKILL.md` files.
|
|
8
|
+
|
|
9
|
+
The skill deliberately contains no command inventory. Agents discover the exact command surface
|
|
10
|
+
from the CLI itself (`commands --json`, `schema <command>`), so the skill stays valid as commands
|
|
11
|
+
evolve; it carries only the knowledge that runtime discovery cannot provide. The skill itself lives
|
|
12
|
+
at [`skills/foundry-world-editor/SKILL.md`](../skills/foundry-world-editor/SKILL.md).
|
|
13
|
+
|
|
14
|
+
## Installing
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
fvtt-world-cli skill install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The default installation delegates to the ecosystem's skills CLI (`npx skills add`), which detects
|
|
21
|
+
the agents present on the machine. It keeps one canonical copy under `~/.agents/skills` — the
|
|
22
|
+
vendor-neutral location of the Agent Skills standard — and points each agent's own skill directory
|
|
23
|
+
at it, so every agent reads the same single copy.
|
|
24
|
+
|
|
25
|
+
An explicit destination works without the skills CLI or network access:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
fvtt-world-cli skill install --to <skills-directory>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`--to` performs a direct copy and records the location in the CLI's local configuration, so that
|
|
32
|
+
copy participates in updates later. `--link` symlinks instead of copying, which keeps the installed
|
|
33
|
+
skill permanently identical to the CLI it came from.
|
|
34
|
+
|
|
35
|
+
## Staying up to date
|
|
36
|
+
|
|
37
|
+
The skill is versioned together with the CLI, and the CLI keeps installed copies current on its
|
|
38
|
+
own: updating the npm package refreshes them, and the daemon performs the same check at startup for
|
|
39
|
+
installations updated by other means, such as a git checkout. For updating purposes a copy is one
|
|
40
|
+
of two kinds:
|
|
41
|
+
|
|
42
|
+
- An **unmodified** copy — exactly what some version of the CLI shipped — is replaced silently with
|
|
43
|
+
the current version.
|
|
44
|
+
- A **modified** copy — one with local edits — is never replaced automatically. It produces a
|
|
45
|
+
warning instead, and keeps producing it until the difference is resolved.
|
|
46
|
+
|
|
47
|
+
The explicit update command follows the same rule and can override it:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
fvtt-world-cli skill update
|
|
51
|
+
fvtt-world-cli skill update --force
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The CLI tells an old shipped version apart from a local edit by a content checksum recorded inside
|
|
55
|
+
every installed copy, so an outdated installation is never mistaken for a customized one.
|
|
56
|
+
|
|
57
|
+
## Removing
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fvtt-world-cli skill remove
|
|
61
|
+
fvtt-world-cli skill remove --to <skills-directory>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The default removal uninstalls the canonical copy, the agent links pointing at it, and every
|
|
65
|
+
location recorded for `--to` installations; `--to` removes one location and forgets it.
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fvtt-world-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Safe local CLI and authenticated bridge for automating live Foundry VTT worlds.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "DroiD16",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/DroiD16/fvtt-world-cli.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/DroiD16/fvtt-world-cli#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/DroiD16/fvtt-world-cli/issues"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"fvtt-world-cli": "./bin/fvtt-world-cli.js",
|
|
24
|
+
"worldctl": "./bin/fvtt-world-cli.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/",
|
|
28
|
+
"dist/",
|
|
29
|
+
"docs/",
|
|
30
|
+
"skills/"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "vitest run packages/cli/tests",
|
|
34
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --external:commander --external:pino --external:tsx --external:ws --external:zod --outfile=dist/index.js",
|
|
35
|
+
"typecheck": "tsc --project ../../tsconfig.json",
|
|
36
|
+
"prepack": "npm run build && node ./bin/prepare-package-assets.mjs",
|
|
37
|
+
"postpack": "node ./bin/prepare-package-assets.mjs --clean",
|
|
38
|
+
"postinstall": "node ./bin/sync-installed-skill.mjs"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"commander": "^13.1.0",
|
|
42
|
+
"pino": "^9.6.0",
|
|
43
|
+
"tsx": "^4.23.12",
|
|
44
|
+
"ws": "^8.21.0",
|
|
45
|
+
"zod": "^3.24.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@fvtt-world-cli/protocol": "1.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: foundry-world-editor
|
|
3
|
+
description: Use when a request involves reading or changing anything in a live Foundry VTT world — or mentions fvtt-world-cli, worldctl, or the Foundry bridge/daemon.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Foundry World Editor (fvtt-world-cli)
|
|
7
|
+
|
|
8
|
+
`fvtt-world-cli` talks to a local authenticated daemon that relays typed commands to a bridge
|
|
9
|
+
module running in an open GM Foundry browser session. Every mutation executes through Foundry's
|
|
10
|
+
own Document APIs inside that session. Treat the world as concurrent state: users, game systems,
|
|
11
|
+
modules, and hooks can change it between commands.
|
|
12
|
+
|
|
13
|
+
## Health check
|
|
14
|
+
|
|
15
|
+
Confirm the runtime before planning any work:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
fvtt-world-cli system info --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Proceed only when `.result.bridge.status` is `connected`, and verify the reported world and GM are
|
|
22
|
+
the intended targets. If the daemon is not running, start `fvtt-world-cli bridge serve` as a
|
|
23
|
+
persistent background process (it does not daemonize itself) and leave it running for the whole
|
|
24
|
+
session. If no bridge connects, a GM must have the target world open in a browser with the
|
|
25
|
+
`fvtt-world-cli` module enabled and paired.
|
|
26
|
+
|
|
27
|
+
Pairing is a first-run trust decision that needs the human: ask the GM to open Module Settings →
|
|
28
|
+
Authorization, name the browser in the `Browser label` field, and choose Pair; run
|
|
29
|
+
`fvtt-world-cli auth pending`, confirm the listed Origin, world, GM, browser label, and client id,
|
|
30
|
+
then `auth approve <code> --yes`. Never run bare `fvtt-world-cli auth`: that is the human's
|
|
31
|
+
one-command pairing wait, it blocks until a request arrives or Ctrl+C ends it, and it refuses
|
|
32
|
+
`--json` — hand that command to the GM at their own terminal instead of running it here. A pairing
|
|
33
|
+
belongs to one browser, so the same GM on the same world can hold several records, and the label is
|
|
34
|
+
chosen in the browser and fixed once paired. Labels are not unique and can render as nothing, so the
|
|
35
|
+
client id is what tells concurrent requests apart and confirms that a re-pair replaces the record you
|
|
36
|
+
meant; on a first pair it is newly minted, with nothing to match it against. Non-interactive use
|
|
37
|
+
requires `--yes`, which prints no identity and skips the confirmation prompt: read all five fields
|
|
38
|
+
from `auth pending` first, and never approve a request whose Origin, world, or GM you did not
|
|
39
|
+
expect.
|
|
40
|
+
|
|
41
|
+
`BRIDGE_NOT_READY` means the daemon is reachable but no GM client is connected yet. `BRIDGE_BUSY`
|
|
42
|
+
means another paired browser holds the active bridge slot: clear it with
|
|
43
|
+
`fvtt-world-cli bridge release` and have the GM choose Connect in the module's Authorization
|
|
44
|
+
window or World CLI scene-controls group — do not start a new pairing.
|
|
45
|
+
|
|
46
|
+
## Hard rules
|
|
47
|
+
|
|
48
|
+
- Pass `--json` on every automated call.
|
|
49
|
+
- Never edit Foundry world files on disk and never automate the Foundry browser UI; this CLI is
|
|
50
|
+
the control surface.
|
|
51
|
+
- Address documents by `id`, never by name; names are not unique. Embedded ids are meaningful only
|
|
52
|
+
with their complete parent chain (actor → item → effect, scene → token → item).
|
|
53
|
+
- Ownership changes go through dedicated `*.ownership.set` commands; raw `ownership` is rejected
|
|
54
|
+
in ordinary payloads.
|
|
55
|
+
- The CLI cannot execute JavaScript, write settings, edit compendium packs in place, or reach
|
|
56
|
+
outside the managed file boundary, and executable region-behavior types are rejected on write.
|
|
57
|
+
Do not look for workarounds; report the limitation instead.
|
|
58
|
+
|
|
59
|
+
## The working loop
|
|
60
|
+
|
|
61
|
+
Discover → inspect schema → locate → read → smallest patch → dry-run → commit → verify. Never
|
|
62
|
+
guess a command name or parameter.
|
|
63
|
+
|
|
64
|
+
1. `fvtt-world-cli commands --json` lists the installed commands and marks mutations. Dotted
|
|
65
|
+
protocol names map to spaced CLI subcommands (`actor.item.update` → `actor item update`). A
|
|
66
|
+
command present in the registry but not advertised by the connected bridge is a client/bridge
|
|
67
|
+
version mismatch.
|
|
68
|
+
2. `fvtt-world-cli schema <command>` shows the exact request parameters, required fields, enums,
|
|
69
|
+
and whether unknown fields are accepted; `fvtt-world-cli <command path> --help` maps flags.
|
|
70
|
+
3. Locate targets with the narrowest query: a family `list --name <substring> --limit <n>` when
|
|
71
|
+
the family is known, `world.search` when it is not, `compendium.index` for one pack. Page with
|
|
72
|
+
`limit`/`offset` until `hasMore` is false. List and search rows are lean discovery projections,
|
|
73
|
+
never the basis of a write.
|
|
74
|
+
4. `get` the complete target before mutating and work from the returned ids.
|
|
75
|
+
5. Build the smallest valid patch. Nested objects merge recursively; plain arrays replace
|
|
76
|
+
wholesale; dotted paths reach permitted leaves; deletion operators remove permitted keys. Never
|
|
77
|
+
send a `get` result back as a patch — it contains read-only and derived fields the update
|
|
78
|
+
schema rejects. For arrays and extensible `system`/`flags` data: read, preserve everything that
|
|
79
|
+
stays, change only the intended part.
|
|
80
|
+
6. Dry-run every nontrivial mutation with the global `--dry-run` flag. It runs the same
|
|
81
|
+
validation, sanitization, permission, capability, and security checks, then stops before
|
|
82
|
+
persistence; check both `ok` and `.result.dryRun`. A preview reports only what is knowable
|
|
83
|
+
before execution and reserves nothing.
|
|
84
|
+
7. Commit with the same content. Attach `--idempotency-key <stable-key>` to any create, clone,
|
|
85
|
+
import, upload, or action that might be retried — one stable key per logical operation.
|
|
86
|
+
8. Verify with a fresh read. This matters most after open-schema writes, bulk operations, and
|
|
87
|
+
actions, where confirmation can say less than an observed post-state.
|
|
88
|
+
|
|
89
|
+
The whole loop in miniature:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
fvtt-world-cli actor list --name "Goblin" --limit 10 --json
|
|
93
|
+
fvtt-world-cli actor get --actor-id <id> --json
|
|
94
|
+
fvtt-world-cli --dry-run actor update --actor-id <id> --name "Goblin Scout" --json
|
|
95
|
+
fvtt-world-cli actor update --actor-id <id> --name "Goblin Scout" --json
|
|
96
|
+
fvtt-world-cli actor get --actor-id <id> --json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Reading results
|
|
100
|
+
|
|
101
|
+
Success responses use `{ok: true, result}` and failures `{ok: false, error: {code, message,
|
|
102
|
+
details}}`, alongside `protocolVersion`, `type`, and the correlation `id` (echoing your `id` in
|
|
103
|
+
`exec --stdin` batches). Branch on `error.code`, never on message text.
|
|
104
|
+
Documents live under a type-named key inside `result` that varies between commands but is stable
|
|
105
|
+
for each one. `id` is the public identifier; a `_id` mirror may accompany it. Values requested
|
|
106
|
+
through `include` flags may be derived or version-dependent — never write them back unless the
|
|
107
|
+
update schema explicitly accepts them.
|
|
108
|
+
|
|
109
|
+
## Retry classification
|
|
110
|
+
|
|
111
|
+
Classify a failure before reacting:
|
|
112
|
+
|
|
113
|
+
- Usage or validation error — fix the request.
|
|
114
|
+
- Permission, safety, or capability error — a different operation or runtime is needed; retrying
|
|
115
|
+
the same call cannot succeed.
|
|
116
|
+
- Not forwarded (connection refused, `BRIDGE_NOT_READY`) — safe to retry once the stack is
|
|
117
|
+
restored.
|
|
118
|
+
- Forwarded but unresolved (`BRIDGE_TIMEOUT`, `BRIDGE_DISCONNECTED`, a timeout after send) — the
|
|
119
|
+
mutation may already have committed: inspect world state first, and reuse the same idempotency
|
|
120
|
+
key when retrying the same logical request. Never mint a new key because a response was lost.
|
|
121
|
+
- Structured Foundry rejection — correct the content and resubmit as a new operation.
|
|
122
|
+
|
|
123
|
+
## Bulk writes and actions
|
|
124
|
+
|
|
125
|
+
Bulk commands (`create-many`, `update-many`, `delete-many`) share single-command validation but
|
|
126
|
+
are not transactions: check the top-level `complete` flag and inspect every entry in `outcomes`.
|
|
127
|
+
Persisted statuses such as `created`, `updated`, and `deleted` mean something different from
|
|
128
|
+
`unchanged`, `alreadyDeleted`, `dropped`, and `unknown`; the authoritative status set lives in the
|
|
129
|
+
protocol constants, not here. Action commands
|
|
130
|
+
(playback, draws, deals, combat transitions, thumbnails, fog) call fixed typed Foundry methods and
|
|
131
|
+
can trigger hooks, chat output, and system automation; read the schema, dry-run first, and
|
|
132
|
+
interpret confirmation fields literally rather than assuming an ordinary update happened.
|
|
133
|
+
|
|
134
|
+
## Managed files
|
|
135
|
+
|
|
136
|
+
File commands address Foundry's managed `data` source only, and writes are confined to the active
|
|
137
|
+
world's allowed tree. A file mutation never rewrites document fields: upload or move the asset,
|
|
138
|
+
take the normalized returned path, update each document reference with an explicit document
|
|
139
|
+
command, verify, and only then delete the old file. `world.audit-files` finds references to
|
|
140
|
+
missing assets; it is not an orphan-file collector.
|
|
141
|
+
|
|
142
|
+
## Context hygiene
|
|
143
|
+
|
|
144
|
+
Large worlds overflow agent context fast. Use server-side filters and small pages, project
|
|
145
|
+
responses with targeted `jq` expressions, fetch bounded id sets with `get-many`, and send
|
|
146
|
+
independent batches through `exec --stdin` (one NDJSON response per request, each judged on its
|
|
147
|
+
own). Request derived fields only when needed, and never dump a whole world, search index, or
|
|
148
|
+
binary file into context.
|
|
149
|
+
|
|
150
|
+
## Deeper reference
|
|
151
|
+
|
|
152
|
+
`fvtt-world-cli docs` lists the durable contract documents shipped with the CLI, and
|
|
153
|
+
`fvtt-world-cli docs <name>` prints one: `commands` (human overview and workflows), `protocol`
|
|
154
|
+
(delivery, error, and session semantics), `security` (trust boundary), `compatibility` (Foundry
|
|
155
|
+
version differences), `getting-started` (first-run pairing walkthrough). The runtime registry —
|
|
156
|
+
`commands --json` and `schema` — always outranks prose.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9551cf818565ba2d70903bb4a28425b233c2b13cbc93625d61b37eb76ef0e0c4
|