fvtt-world-cli 1.1.0 → 1.1.2
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/README.md +37 -36
- package/dist/index.js +12 -2
- package/docs/README.md +5 -61
- package/docs/architecture.md +53 -209
- package/docs/commands.md +166 -444
- package/docs/compatibility.md +13 -22
- package/docs/getting-started.md +76 -74
- package/docs/images/module-settings.png +0 -0
- package/docs/protocol.md +228 -187
- package/docs/security.md +149 -322
- package/docs/skill.md +25 -30
- package/package.json +17 -6
- package/skills/foundry-world-editor/SKILL.md +8 -0
- package/skills/foundry-world-editor/SKILL.md.sha256 +1 -1
- package/bin/sync-installed-skill.mjs +0 -89
- package/docs/images/icon.svg +0 -16
- package/docs/images/mark.svg +0 -15
package/docs/skill.md
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
# Agent skill
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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).
|
|
3
|
+
The package includes [foundry-world-editor](../skills/foundry-world-editor/SKILL.md), operating
|
|
4
|
+
instructions for AI agents that support Agent Skills.
|
|
13
5
|
|
|
14
6
|
## Installing
|
|
15
7
|
|
|
@@ -17,10 +9,8 @@ at [`skills/foundry-world-editor/SKILL.md`](../skills/foundry-world-editor/SKILL
|
|
|
17
9
|
fvtt-world-cli skill install
|
|
18
10
|
```
|
|
19
11
|
|
|
20
|
-
The default installation
|
|
21
|
-
|
|
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.
|
|
12
|
+
The default installation runs `npx skills add`, which detects the agents present on the machine.
|
|
13
|
+
It keeps a shared copy under `~/.agents/skills` and links the agents' skill directories to it.
|
|
24
14
|
|
|
25
15
|
An explicit destination works without the skills CLI or network access:
|
|
26
16
|
|
|
@@ -28,31 +18,27 @@ An explicit destination works without the skills CLI or network access:
|
|
|
28
18
|
fvtt-world-cli skill install --to <skills-directory>
|
|
29
19
|
```
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
skill permanently identical to the CLI it came from.
|
|
21
|
+
The CLI records this destination for later updates. Add `--link` to use the packaged skill
|
|
22
|
+
directly through a symlink:
|
|
34
23
|
|
|
35
|
-
|
|
24
|
+
```bash
|
|
25
|
+
fvtt-world-cli skill install --to <skills-directory> --link
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`--link` requires `--to`. A linked skill follows CLI package updates automatically.
|
|
36
29
|
|
|
37
|
-
|
|
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 updates, a copy is one
|
|
40
|
-
of two kinds:
|
|
30
|
+
## Staying up to date
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
32
|
+
At startup, the daemon updates unmodified copies in the shared directory and recorded `--to`
|
|
33
|
+
locations. It preserves locally edited copies and warns instead of overwriting them.
|
|
46
34
|
|
|
47
|
-
|
|
35
|
+
To update manually:
|
|
48
36
|
|
|
49
37
|
```bash
|
|
50
38
|
fvtt-world-cli skill update
|
|
51
|
-
fvtt-world-cli skill update --force
|
|
52
39
|
```
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
every installed copy, so an outdated installation is never mistaken for a customized one.
|
|
41
|
+
Add `--force` to replace a locally edited copy with the packaged version.
|
|
56
42
|
|
|
57
43
|
## Removing
|
|
58
44
|
|
|
@@ -63,3 +49,12 @@ fvtt-world-cli skill remove --to <skills-directory>
|
|
|
63
49
|
|
|
64
50
|
The default removal uninstalls the canonical copy, the agent links pointing at it, and every
|
|
65
51
|
location recorded for `--to` installations; `--to` removes one location and forgets it.
|
|
52
|
+
|
|
53
|
+
## JSON output
|
|
54
|
+
|
|
55
|
+
For `skill install`, `skill update`, and `skill remove`, `--json` requires an explicit `--to`
|
|
56
|
+
destination:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
fvtt-world-cli skill install --to <skills-directory> --json
|
|
60
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fvtt-world-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Safe local CLI and authenticated bridge for automating live Foundry VTT worlds.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"foundry-vtt",
|
|
7
|
+
"foundryvtt",
|
|
8
|
+
"fvtt",
|
|
9
|
+
"virtual-tabletop",
|
|
10
|
+
"ttrpg",
|
|
11
|
+
"cli",
|
|
12
|
+
"automation",
|
|
13
|
+
"ai",
|
|
14
|
+
"ai-agent",
|
|
15
|
+
"agent-skills"
|
|
16
|
+
],
|
|
5
17
|
"license": "MIT",
|
|
6
18
|
"author": "DroiD16",
|
|
7
19
|
"repository": {
|
|
@@ -20,8 +32,8 @@
|
|
|
20
32
|
},
|
|
21
33
|
"type": "module",
|
|
22
34
|
"bin": {
|
|
23
|
-
"fvtt-world-cli": "
|
|
24
|
-
"worldctl": "
|
|
35
|
+
"fvtt-world-cli": "bin/fvtt-world-cli.js",
|
|
36
|
+
"worldctl": "bin/fvtt-world-cli.js"
|
|
25
37
|
},
|
|
26
38
|
"files": [
|
|
27
39
|
"bin/",
|
|
@@ -34,8 +46,7 @@
|
|
|
34
46
|
"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
47
|
"typecheck": "tsc --project ../../tsconfig.json",
|
|
36
48
|
"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"
|
|
49
|
+
"postpack": "node ./bin/prepare-package-assets.mjs --clean"
|
|
39
50
|
},
|
|
40
51
|
"dependencies": {
|
|
41
52
|
"commander": "^13.1.0",
|
|
@@ -45,6 +56,6 @@
|
|
|
45
56
|
"zod": "^3.24.3"
|
|
46
57
|
},
|
|
47
58
|
"devDependencies": {
|
|
48
|
-
"@fvtt-world-cli/protocol": "1.1.
|
|
59
|
+
"@fvtt-world-cli/protocol": "1.1.2"
|
|
49
60
|
}
|
|
50
61
|
}
|
|
@@ -155,8 +155,16 @@ Classify a failure before reacting:
|
|
|
155
155
|
- `APPROVAL_DENIED`, `APPROVAL_TIMEOUT`, and `APPROVAL_CANCELLED` mean nothing ran. Report the outcome
|
|
156
156
|
and wait for user direction. `APPROVAL_QUEUE_FULL` also means nothing ran, but it can clear after
|
|
157
157
|
earlier requests settle.
|
|
158
|
+
- `APPROVAL_STALE` means the GM allowed a macro execution whose body or type changed — or whose
|
|
159
|
+
macro was deleted — after it was shown, so nothing ran. Read the macro again and re-send the execution if the current content is
|
|
160
|
+
still what the user wants; that opens a fresh approval.
|
|
158
161
|
- `APPROVAL_UNKNOWN` and unconfirmed cancellation are indeterminate. Read the affected documents,
|
|
159
162
|
report the result, and use a fresh key if the operation still needs to run.
|
|
163
|
+
- For an update `INTERNAL_ERROR` with `details.partial: true` or `details.indeterminate: true`,
|
|
164
|
+
read the affected document before retrying. `changedFields` names requested top-level fields
|
|
165
|
+
observed to change, not fully applied nested values. An embedded creation without a requested
|
|
166
|
+
id may already exist despite an indeterminate error. Send only the remaining changes as a new
|
|
167
|
+
operation with a fresh idempotency key, if using one.
|
|
160
168
|
- Correct a structured Foundry rejection and submit the corrected content as a new operation.
|
|
161
169
|
|
|
162
170
|
## Bulk writes and actions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d1757739e55e01589814ed919912bbe5423afa99aee16a1422fbaee5bed11c8a
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { cpSync, lstatSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
3
|
-
import { homedir, platform } from "node:os";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const SKILL_NAME = "foundry-world-editor";
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const binDirectory = dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const sourceDirectory = [resolve(binDirectory, "../skills"), resolve(binDirectory, "../../../skills")]
|
|
12
|
-
.map((candidate) => resolve(candidate, SKILL_NAME))
|
|
13
|
-
.find((candidate) => {
|
|
14
|
-
try {
|
|
15
|
-
return statSync(join(candidate, "SKILL.md")).isFile();
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
if (!sourceDirectory) process.exit(0);
|
|
21
|
-
const packaged = readFileSync(join(sourceDirectory, "SKILL.md"), "utf8");
|
|
22
|
-
|
|
23
|
-
const home = process.env.HOME?.trim() || homedir();
|
|
24
|
-
const xdgHome = process.env.XDG_CONFIG_HOME?.trim();
|
|
25
|
-
const configPath = xdgHome
|
|
26
|
-
? join(xdgHome, "fvtt-world-cli", "config.json")
|
|
27
|
-
: platform() === "darwin"
|
|
28
|
-
? join(home, "Library", "Application Support", "fvtt-world-cli", "config.json")
|
|
29
|
-
: platform() === "win32"
|
|
30
|
-
? join(
|
|
31
|
-
process.env.APPDATA?.trim() || join(home, "AppData", "Roaming"),
|
|
32
|
-
"fvtt-world-cli",
|
|
33
|
-
"config.json"
|
|
34
|
-
)
|
|
35
|
-
: join(home, ".config", "fvtt-world-cli", "config.json");
|
|
36
|
-
|
|
37
|
-
const roots = new Set([join(home, ".agents", "skills")]);
|
|
38
|
-
try {
|
|
39
|
-
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
40
|
-
if (Array.isArray(config?.skillInstalls)) {
|
|
41
|
-
for (const root of config.skillInstalls) {
|
|
42
|
-
if (typeof root === "string" && root.length > 0) roots.add(root);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// no or unreadable config: the standard location is still checked
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const root of roots) {
|
|
50
|
-
try {
|
|
51
|
-
const destination = join(root, SKILL_NAME);
|
|
52
|
-
const existing = (() => {
|
|
53
|
-
try {
|
|
54
|
-
return lstatSync(destination);
|
|
55
|
-
} catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
})();
|
|
59
|
-
if (!existing || existing.isSymbolicLink()) continue;
|
|
60
|
-
|
|
61
|
-
const installed = readFileSync(join(destination, "SKILL.md"), "utf8");
|
|
62
|
-
if (installed === packaged) continue;
|
|
63
|
-
|
|
64
|
-
const recorded = (() => {
|
|
65
|
-
try {
|
|
66
|
-
return readFileSync(join(destination, "SKILL.md.sha256"), "utf8").trim();
|
|
67
|
-
} catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
})();
|
|
71
|
-
|
|
72
|
-
if (recorded === createHash("sha256").update(installed).digest("hex")) {
|
|
73
|
-
rmSync(destination, { recursive: true, force: true });
|
|
74
|
-
cpSync(sourceDirectory, destination, { recursive: true });
|
|
75
|
-
console.error(
|
|
76
|
-
`fvtt-world-cli: updated the installed agent skill at ${destination} to the packaged version.`
|
|
77
|
-
);
|
|
78
|
-
} else {
|
|
79
|
-
console.error(
|
|
80
|
-
`fvtt-world-cli: the installed agent skill at ${destination} has local modifications and was NOT updated. Compare them, then run \`fvtt-world-cli skill update --force\` to replace it.`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|
package/docs/images/icon.svg
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
-
<rect width="512" height="512" fill="#0b0a13"/>
|
|
3
|
-
<g fill="none" stroke="#fc6119" stroke-linecap="round" stroke-linejoin="round">
|
|
4
|
-
<g stroke-width="15">
|
|
5
|
-
<path d="M256 36 446.53 146V366L256 476 65.47 366V146Z"/>
|
|
6
|
-
<path d="M256 120 373.78 324H138.22Z"/>
|
|
7
|
-
<path d="M256 36 256 120M446.53 146 256 120M65.47 146 256 120"/>
|
|
8
|
-
<path d="M446.53 366 373.78 324M446.53 146 373.78 324M256 476 373.78 324"/>
|
|
9
|
-
<path d="M65.47 366 138.22 324M65.47 146 138.22 324M256 476 138.22 324"/>
|
|
10
|
-
</g>
|
|
11
|
-
<g stroke-width="12">
|
|
12
|
-
<path d="M223 240 251 268 223 296"/>
|
|
13
|
-
<path d="M266 296H306" stroke-linecap="butt"/>
|
|
14
|
-
</g>
|
|
15
|
-
</g>
|
|
16
|
-
</svg>
|
package/docs/images/mark.svg
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
-
<g fill="none" stroke="#fc6119" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
-
<g stroke-width="15">
|
|
4
|
-
<path d="M256 36 446.53 146V366L256 476 65.47 366V146Z"/>
|
|
5
|
-
<path d="M256 120 373.78 324H138.22Z"/>
|
|
6
|
-
<path d="M256 36 256 120M446.53 146 256 120M65.47 146 256 120"/>
|
|
7
|
-
<path d="M446.53 366 373.78 324M446.53 146 373.78 324M256 476 373.78 324"/>
|
|
8
|
-
<path d="M65.47 366 138.22 324M65.47 146 138.22 324M256 476 138.22 324"/>
|
|
9
|
-
</g>
|
|
10
|
-
<g stroke-width="12">
|
|
11
|
-
<path d="M223 240 251 268 223 296"/>
|
|
12
|
-
<path d="M266 296H306" stroke-linecap="butt"/>
|
|
13
|
-
</g>
|
|
14
|
-
</g>
|
|
15
|
-
</svg>
|