@passioncode-ai/passioncode 0.2.48 → 0.2.49

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.49 - 2026-10-28
4
+
5
+ ### Fixed
6
+
7
+ - On Windows the launcher makes its links (`current`, the skill hub and each agent's channel) as
8
+ junctions instead of symbolic links, so a member without an administrator session or Developer
9
+ Mode can install and update the skill set; a junction's target is read back in the same form the
10
+ launcher compares (PC-87).
11
+
3
12
  ## 0.2.48 - 2026-10-28
4
13
 
5
14
  ### Fixed
package/README.md CHANGED
@@ -76,7 +76,8 @@ secret into a product's own hidden prompt, starting Docker for Fabric, and resta
76
76
  comes from it.
77
77
  - **Every other agent** (Cursor, Codex, Gemini, OpenCode, Kiro, Windsurf, Goose, …) gets
78
78
  the same skills through `~/.agents/skills`, linked from each channel that exists on
79
- the machine.
79
+ the machine. On Windows these links (and `~/.passioncode/current`) are junctions, which
80
+ need neither an administrator session nor Developer Mode.
80
81
  - Updates create no skill copies in `~/.claude/skills`: a plain copy there shadows the plugin. Such
81
82
  copies, and hub entries that pointed at a repository's working tree, are moved into
82
83
  `~/.passioncode/quarantine/` — `npx @passioncode-ai/passioncode@latest restore` puts them back.
package/SECURITY.md CHANGED
@@ -5,7 +5,7 @@ carries credentials or a working exploit.
5
5
 
6
6
  What `passioncode update` touches (setup and doctor: [below](#what-setup-and-doctor-touch)): `~/.passioncode/` (releases, state, quarantine,
7
7
  logs); Claude Code's plugin registry through the `claude plugin` CLI only;
8
- `~/.agents/skills/<member skill>` and `<agent channel>/<member skill>` links; and
8
+ `~/.agents/skills/<member skill>` and `<agent channel>/<member skill>` links (junctions on Windows); and
9
9
  `~/.claude/skills/<member skill>`, which it moves into the quarantine once the plugin
10
10
  that replaces it is verified. For a member's old single-member marketplace
11
11
  (`legacyMarketplaces` in `family.json`), once that plugin is verified and nothing else
package/lib/launcher.js CHANGED
@@ -55,7 +55,25 @@ function writeText(file, text) {
55
55
  fs.renameSync(tmp, file);
56
56
  }
57
57
  function lstat(p) { try { return fs.lstatSync(p); } catch (_) { return null; } }
58
- function linkTarget(p) { try { return fs.readlinkSync(p); } catch (_) { return null; } }
58
+ /**
59
+ * A link's target as written. A Windows junction reads back absolute, sometimes behind `\\?\` or with a
60
+ * trailing separator, so there it is put in the form `path.resolve` gives (PC-87).
61
+ */
62
+ function linkTarget(p) {
63
+ let target;
64
+ try { target = fs.readlinkSync(p); } catch (_) { return null; }
65
+ if (process.platform !== 'win32') return target;
66
+ target = target.replace(/^\\\\\?\\/, '');
67
+ return path.win32.isAbsolute(target) ? path.win32.resolve(target) : target;
68
+ }
69
+ /**
70
+ * Make a directory link. Windows makes a symbolic link only for an administrator or with Developer
71
+ * Mode on, so there it is a junction, which needs neither and always holds an absolute target (PC-87).
72
+ */
73
+ function makeLink(target, at, { platform = process.platform, symlink = fs.symlinkSync } = {}) {
74
+ if (platform !== 'win32') return symlink(target, at);
75
+ return symlink(path.resolve(path.dirname(at), target), at, 'junction');
76
+ }
59
77
  function pointsTo(at, target) { const link = linkTarget(at); return link !== null && path.resolve(path.dirname(at), link) === target; }
60
78
  function isDirectory(at) { try { return fs.statSync(at).isDirectory(); } catch (_) { return false; } }
61
79
  function readState(file, { tolerant = false } = {}) {
@@ -73,12 +91,13 @@ function readState(file, { tolerant = false } = {}) {
73
91
  * On Windows the swap is therefore not atomic: between the unlink and the rename `at` is absent,
74
92
  * and a run killed there leaves no `current` until the next update (status reports it missing).
75
93
  */
76
- function symlinkAtomic(target, at, { platform = process.platform, rename = fs.renameSync, unlink = fs.unlinkSync } = {}) {
94
+ function symlinkAtomic(target, at, { platform = process.platform, rename = fs.renameSync, unlink = fs.unlinkSync, symlink = fs.symlinkSync } = {}) {
95
+ const link = (to, where) => makeLink(to, where, { platform, symlink });
77
96
  fs.mkdirSync(path.dirname(at), { recursive: true });
78
97
  const tmp = `${at}.${process.pid}.lnk`;
79
98
  const dropTmp = () => { try { fs.unlinkSync(tmp); } catch (_) { /* none */ } };
80
99
  dropTmp();
81
- fs.symlinkSync(target, tmp);
100
+ link(target, tmp);
82
101
  try {
83
102
  rename(tmp, at);
84
103
  } catch (error) {
@@ -88,7 +107,7 @@ function symlinkAtomic(target, at, { platform = process.platform, rename = fs.re
88
107
  try { unlink(at); } catch (unlinkError) { dropTmp(); throw unlinkError; }
89
108
  try { rename(tmp, at); } catch (again) {
90
109
  dropTmp();
91
- try { fs.symlinkSync(previous, at); } catch (back) { throw new Error(`${again.message}; and putting back ${at} → ${previous} failed (${back.message})`); }
110
+ try { link(previous, at); } catch (back) { throw new Error(`${again.message}; and putting back ${at} → ${previous} failed (${back.message})`); }
92
111
  throw again;
93
112
  }
94
113
  }
@@ -883,5 +902,5 @@ module.exports = {
883
902
  restore: (opts = {}) => withUpdateLock(opts.home, () => restore(opts)),
884
903
  uninstall: (opts = {}) => withUpdateLock(opts.home, () => uninstall(opts)),
885
904
  status, setConfig, paths, CHANNELS, MARKETPLACE, payloadManifest, marketplaceAddArg,
886
- acquireUpdateLock, UpdateRunning, staleReleases, symlinkAtomic, claudeRunner, bundledMcpServers, mcpFileTargets, readSettings, tomlTableRange, tomlTableCommand, tomlTableArgs, tomlMentions,
905
+ acquireUpdateLock, UpdateRunning, staleReleases, symlinkAtomic, makeLink, linkTarget, claudeRunner, bundledMcpServers, mcpFileTargets, readSettings, tomlTableRange, tomlTableCommand, tomlTableArgs, tomlMentions,
887
906
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/passioncode",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "description": "Set up PassionCode.ai on a computer with one command — the apps from their verified releases, their MCP servers for every coding agent and the PassionCode.ai skills — and check and repair it with the same command or `doctor`.",
5
5
  "bin": {
6
6
  "passioncode": "bin/passioncode.js"
@@ -35,7 +35,7 @@
35
35
  "name": "passioncode",
36
36
  "displayName": "PassionCode.ai",
37
37
  "source": "./plugins/passioncode",
38
- "version": "0.2.48",
38
+ "version": "0.2.49",
39
39
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
40
40
  "author": {
41
41
  "name": "PassionCode.ai",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "family": "passioncode",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "release": true,
5
5
  "members": [
6
6
  {
@@ -62,9 +62,9 @@
62
62
  "name": "passioncode",
63
63
  "displayName": "PassionCode.ai",
64
64
  "repo": "passioncode-ai/passioncode",
65
- "ref": "v0.2.48",
65
+ "ref": "v0.2.49",
66
66
  "commit": null,
67
- "version": "0.2.48",
67
+ "version": "0.2.49",
68
68
  "via": "this repository",
69
69
  "skills": [
70
70
  "setting-up-passioncode",
@@ -72,7 +72,7 @@
72
72
  ],
73
73
  "legacyPluginIds": [],
74
74
  "legacyMarketplaces": [],
75
- "contentHash": "sha256:18c44b353312024f0735136631c2df0ac416bb8f3881406958e5aed0e710375a",
75
+ "contentHash": "sha256:f69e173ef90a590a533a71de1028073a785e59d917a03c1bc0516cdba17698e2",
76
76
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
77
77
  "author": {
78
78
  "name": "PassionCode.ai",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "passioncode",
4
4
  "displayName": "PassionCode.ai",
5
- "version": "0.2.48",
5
+ "version": "0.2.49",
6
6
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check at session start that keeps the PassionCode.ai skill set current.",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",