pennyrouter 0.3.23 → 0.3.25

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
@@ -87,7 +87,8 @@ of `npx pennyrouter install`; to install or refresh it separately, run:
87
87
  npx pennyrouter compare
88
88
  ```
89
89
 
90
- Then use `penny-compare` for a side-by-side run or `penny-session` for one Penny-routed session.
90
+ The installer puts `penny-compare` and `penny-session` on PATH alongside the `penny` launcher.
91
+ Use `penny-compare` for a side-by-side run or `penny-session` for one Penny-routed session.
91
92
  The bundle requires `tmux`, Python 3.10+, and the harnesses being compared.
92
93
 
93
94
  On macOS and Linux, a normal install adds a small `penny` launcher under `~/.local/bin`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
4
4
  "description": "Install and manage PennyRouter local coding-agent integrations.",
5
5
  "homepage": "https://pennyrouter.com",
6
6
  "bugs": {
package/src/launch.js CHANGED
@@ -2,6 +2,7 @@ import { spawn, spawnSync } from "node:child_process";
2
2
  import {
3
3
  chmod,
4
4
  cp,
5
+ lstat,
5
6
  mkdtemp,
6
7
  mkdir,
7
8
  readFile,
@@ -284,6 +285,24 @@ export async function installPennyCompare() {
284
285
  await cp(join(source, entry), join(destination, entry), { recursive: true, force: true });
285
286
  }
286
287
  await mkdir(join(destination, "reports"), { recursive: true, mode: 0o700 });
288
+ await mkdir(pennyBinDir(), { recursive: true, mode: 0o755 });
289
+ for (const entry of ["penny-compare", "penny-session", "penny-compare-viewer"]) {
290
+ const link = join(pennyBinDir(), entry);
291
+ const target = join(destination, entry);
292
+ let replace = false;
293
+ try {
294
+ replace = (await lstat(link)).isSymbolicLink();
295
+ } catch (error) {
296
+ if (error.code !== "ENOENT") throw error;
297
+ }
298
+ if (replace) await unlink(link);
299
+ try {
300
+ await symlink(target, link);
301
+ } catch (error) {
302
+ if (error.code !== "EEXIST") throw error;
303
+ // Leave a user-owned regular file in place rather than overwriting it.
304
+ }
305
+ }
287
306
  return { supported: true, installed: true, path: destination };
288
307
  }
289
308
 
@@ -20,6 +20,8 @@ import {
20
20
  isCodexDesktopRunning,
21
21
  loadPennyConfig,
22
22
  pennyConfigPath,
23
+ pennyCompareDir,
24
+ pennyBinDir,
23
25
  pennyRuntimeDir,
24
26
  pennyShimPath,
25
27
  readClaudeDesktopCredential,
@@ -100,6 +102,10 @@ async function main() {
100
102
  assert.equal(second.changed, false);
101
103
  assert.equal(await fileMode(pennyShimPath()), 0o755);
102
104
  assert.ok(fs.existsSync(path.join(pennyRuntimeDir(), "bin", "penny.js")));
105
+ assert.equal(
106
+ fs.realpathSync(path.join(pennyBinDir(), "penny-compare")),
107
+ fs.realpathSync(path.join(pennyCompareDir(), "penny-compare")),
108
+ );
103
109
  assert.ok(!fs.existsSync(staleSkill), "runtime upgrade removes stale plugin files");
104
110
  const rc = fs.readFileSync(path.join(home, ".zshrc"), "utf8");
105
111
  assert.equal((rc.match(/>>> pennyrouter PATH >>>/g) || []).length, 1);
@@ -118,7 +118,7 @@ else
118
118
  left="$(choose 'left pane' claude "${variants[@]}")"
119
119
  echo >&2
120
120
  if [ -z "$dir_left" ]; then
121
- echo " left directory (blank = $PWD)" >&2
121
+ echo " left directory (blank = $PWD/native)" >&2
122
122
  read -r -p " [] " dir_left >&2 || true
123
123
  echo >&2
124
124
  fi
@@ -130,7 +130,7 @@ else
130
130
  right="$(choose 'right pane' penny-claude "${variants[@]}")"
131
131
  echo >&2
132
132
  if [ -z "$dir_right" ]; then
133
- echo " right directory (blank = $PWD)" >&2
133
+ echo " right directory (blank = $PWD/penny)" >&2
134
134
  read -r -p " [] " dir_right >&2 || true
135
135
  echo >&2
136
136
  fi
@@ -232,9 +232,9 @@ export PENNY_COMPARE_STATE="$state"
232
232
 
233
233
  # Each pane's readout finds its session by the directory it ran in, so the two must be set
234
234
  # explicitly rather than left to tmux's default of wherever the launcher was invoked.
235
- workdir_left="${dir_left:-$PWD}"
235
+ workdir_left="${dir_left:-$PWD/native}"
236
236
  workdir_left="${workdir_left/#\~/$HOME}"
237
- workdir_right="${dir_right:-$PWD}"
237
+ workdir_right="${dir_right:-$PWD/penny}"
238
238
  workdir_right="${workdir_right/#\~/$HOME}"
239
239
 
240
240
  # --native-proxy only means anything on a NATIVE Claude or Codex pane;