fvtt-world-cli 1.1.0 → 1.1.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.
package/README.md CHANGED
@@ -100,8 +100,9 @@ The package ships `foundry-world-editor`, an installable [Agent Skill](docs/skil
100
100
  an agent to drive the CLI safely. It follows the open Agent Skills standard, so the same skill
101
101
  works in Claude Code, Codex, and other agents that read `SKILL.md` files.
102
102
 
103
- An installed skill stays current on its own: updating the package refreshes it. A copy you have
104
- edited locally is never replaced automatically, so your own instructions survive updates.
103
+ An installed skill stays current on its own: after a package update, the bridge daemon refreshes
104
+ it at startup. A copy you have edited locally is never replaced automatically, so your own
105
+ instructions survive updates.
105
106
 
106
107
  ## Built to be trusted with a live world
107
108
 
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { readFileSync as readFileSync2 } from "node:fs";
10
10
  import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
11
11
 
12
12
  // ../protocol/src/constants.js
13
- var PROTOCOL_VERSION = "1.1.0";
13
+ var PROTOCOL_VERSION = "1.1.1";
14
14
  var PROTOCOL_COMPONENTS = Object.freeze({
15
15
  MODULE: "module",
16
16
  CLI_DAEMON: "cli-daemon",
@@ -14699,10 +14699,12 @@ function syncInstalledSkillCopies(dependencies) {
14699
14699
  try {
14700
14700
  const sourceDirectory = resolveSkillSourceDirectory();
14701
14701
  if (!sourceDirectory) return;
14702
+ let installedAnywhere = false;
14702
14703
  for (const root of knownSkillRoots(dependencies)) {
14703
14704
  try {
14704
14705
  const destination = join2(root, SKILL_NAME);
14705
14706
  const state = inspectInstalledSkill(destination, sourceDirectory);
14707
+ if (state !== "missing") installedAnywhere = true;
14706
14708
  if (state === "pristine") {
14707
14709
  rmSync(destination, { recursive: true, force: true });
14708
14710
  cpSync(sourceDirectory, destination, { recursive: true });
@@ -14722,6 +14724,13 @@ function syncInstalledSkillCopies(dependencies) {
14722
14724
  continue;
14723
14725
  }
14724
14726
  }
14727
+ if (!installedAnywhere) {
14728
+ write(
14729
+ dependencies.stderr,
14730
+ `The ${SKILL_NAME} agent skill is not installed. If AI agents use this CLI, consider running \`fvtt-world-cli skill install\` to give them its operating manual.
14731
+ `
14732
+ );
14733
+ }
14725
14734
  } catch {
14726
14735
  return;
14727
14736
  }
package/docs/skill.md CHANGED
@@ -32,12 +32,16 @@ fvtt-world-cli skill install --to <skills-directory>
32
32
  copy participates in updates later. `--link` symlinks instead of copying, which keeps the installed
33
33
  skill permanently identical to the CLI it came from.
34
34
 
35
+ A bridge daemon started while no copy is installed anywhere prints a reminder with the
36
+ installation command, so a missing skill does not go unnoticed.
37
+
35
38
  ## Staying up to date
36
39
 
37
40
  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:
41
+ own: the bridge daemon checks every known copy at startup and refreshes outdated ones, so a package
42
+ update takes effect the next time the daemon starts. The check runs inside the CLI itself; nothing
43
+ about installation or updates relies on npm lifecycle scripts, which npm blocks by default. For
44
+ updates, a copy is one of two kinds:
41
45
 
42
46
  - An **unmodified** copy, exactly what some version of the CLI shipped, is replaced silently with
43
47
  the current version.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fvtt-world-cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Safe local CLI and authenticated bridge for automating live Foundry VTT worlds.",
5
5
  "license": "MIT",
6
6
  "author": "DroiD16",
@@ -20,8 +20,8 @@
20
20
  },
21
21
  "type": "module",
22
22
  "bin": {
23
- "fvtt-world-cli": "./bin/fvtt-world-cli.js",
24
- "worldctl": "./bin/fvtt-world-cli.js"
23
+ "fvtt-world-cli": "bin/fvtt-world-cli.js",
24
+ "worldctl": "bin/fvtt-world-cli.js"
25
25
  },
26
26
  "files": [
27
27
  "bin/",
@@ -34,8 +34,7 @@
34
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
35
  "typecheck": "tsc --project ../../tsconfig.json",
36
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"
37
+ "postpack": "node ./bin/prepare-package-assets.mjs --clean"
39
38
  },
40
39
  "dependencies": {
41
40
  "commander": "^13.1.0",
@@ -45,6 +44,6 @@
45
44
  "zod": "^3.24.3"
46
45
  },
47
46
  "devDependencies": {
48
- "@fvtt-world-cli/protocol": "1.1.0"
47
+ "@fvtt-world-cli/protocol": "1.1.1"
49
48
  }
50
49
  }
@@ -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
- }