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/docs/skill.md CHANGED
@@ -1,15 +1,7 @@
1
1
  # Agent skill
2
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).
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 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.
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
- `--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.
21
+ The CLI records this destination for later updates. Add `--link` to use the packaged skill
22
+ directly through a symlink:
34
23
 
35
- ## Staying up to date
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
- 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 updates, a copy is one
40
- of two kinds:
30
+ ## Staying up to date
41
31
 
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.
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
- The explicit update command follows the same rule and can override it:
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
- 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.
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.0",
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": "./bin/fvtt-world-cli.js",
24
- "worldctl": "./bin/fvtt-world-cli.js"
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.0"
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
- 1834777e1d540cb4cc11b6eac862ff99878a6e4920ebbc1e2599871893e19dc9
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
- }
@@ -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>
@@ -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>