claude-buddy-reroll 1.8.18 → 1.9.0

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
@@ -1,173 +1,173 @@
1
- # claude-buddy-reroll
2
-
3
- [![npm](https://img.shields.io/npm/v/claude-buddy-reroll?style=for-the-badge&color=cb3837)](https://www.npmjs.com/package/claude-buddy-reroll)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](LICENSE)
5
-
6
- > 쪼아요 쪼아요~ 버디 가챠 쪼아요~
7
- >
8
- > 맘에 안 드는 버디? 스피키가 npm 터미널에서 다시 뽑아줄 거예요!
9
-
10
- ---
11
-
12
- ## 설치
13
-
14
- ```bash
15
- npm i -g claude-buddy-reroll
16
- ```
17
-
18
- 설치하면 `buddy`와 `bdy` 명령어가 생겨요.
19
-
20
- 기본 화면은 스피키 스타일의 리치 터미널 홈으로 시작하고, `dex`는 발견한 개체를 다시 적용할 수 있게 설계돼 있어요.
21
- 지금은 `fullscreen dex`, `profile lens chooser`, `animated preview`, `manual form focus`, `form collection`, `one-by-one gacha reveal`까지 들어가 있어요.
22
- 여러 Claude 계정이 있어도 현재 계정을 직접 읽어서 계정별 프로필 상태를 따로 저장해요.
23
-
24
- 처음 설치할 때 런타임 셋업이 자동으로 Bun과 Claude 연동을 준비해요. 자동 셋업이 실패했거나 `Bun is required` 에러가 보이면 아래를 한 번 실행하세요.
25
-
26
- ```bash
27
- bdy setup
28
- ```
29
-
30
- `bdy update`도 업데이트 후 같은 셋업을 자동으로 다시 확인해요.
31
- 그리고 현재 계정 기준으로 도감 정합성도 다시 맞춰요.
32
-
33
- ---
34
-
35
- ## CLI 사용법
36
-
37
- ```bash
38
- bdy # 스피키 홈 / 명령 요약
39
- bdy check # 내 버디 확인
40
- bdy gacha # 10연차 가챠!
41
- bdy gacha 20 # 20연차!
42
- bdy reroll # 리롤 (인터랙티브 버디 교체)
43
- bdy dex # fullscreen 도감 + 프로필 렌즈 + 수동 폼 포커스 + 발견 개체 재적용
44
- bdy doctor # profile / dex apply 진단
45
- bdy restore # 원래 버디로 복원
46
- bdy setup # Bun / 런타임 셋업 복구
47
- ```
48
-
49
- ## 지금 버전의 방향
50
-
51
- - npm-only 제품 표면
52
- - 스피키 감성의 richer terminal UI
53
- - species-only가 아니라 discovered variant를 우선 재적용하는 dex
54
- - species progress와 form collection을 분리한 도감
55
- - 10연차 one-by-one reveal + highlight-aware skip
56
- - 오른쪽 상세 패널에 animated preview / profile lens / rarity track / form gallery
57
- - Dex에서 `1-4`로 폼 포커스, `0`으로 auto-cycle 복귀
58
- - 현재 Claude 계정을 매번 직접 읽고 계정별 프로필 상태를 분리 저장
59
- - 저장된 프로필 데이터를 별도 렌즈로 열어보고 현재 감지된 계정에 다시 적용 가능
60
- - `doctor`로 profile / dex preview vs apply 후보 비교 가능
61
- - JSON 모드는 유지해서 에이전트/스크립트 자동화도 가능
62
-
63
- ### Agent DX (JSON 모드)
64
-
65
- 에이전트/스크립트에서 쓸 때:
66
-
67
- ```bash
68
- bdy check --json # 구조화된 JSON 출력
69
- bdy gacha 5 --json --fields species,rarity # 필드 필터링
70
- bdy reroll --json # 후보만 출력
71
- bdy reroll --json --pick 3 # 3번 자동 선택 + 패치
72
- bdy reroll --json --pick 3 --dry-run # 미리보기만
73
- bdy doctor --json # 현재 profile 진단
74
- bdy doctor dex 13 --json # 특정 도감 항목 preview/apply 비교
75
- bdy schema # 전체 커맨드 JSON Schema
76
- bdy schema gacha # 특정 커맨드 스키마
77
- ```
78
-
79
- | 플래그 | 설명 |
80
- |--------|------|
81
- | `--json` | stdout에 JSON, 로그는 stderr |
82
- | `--pick N` | N번째 자동 선택 (readline 스킵) |
83
- | `--dry-run` | 패치 미리보기 (reroll 전용) |
84
- | `--fields a,b` | 출력 필드 필터링 |
85
- | `--limit N` | 가챠 횟수 지정 |
86
-
87
- ## 이게 뭐예요?
88
-
89
- Claude Code의 `/buddy`는 계정 ID로 버디가 **고정**돼요.
90
-
91
- ```
92
- 버디 = hash(내_계정 + SALT)
93
- ```
94
-
95
- 스피키가 SALT를 바꿔주면 **완전히 다른 버디**가 나와요!
96
-
97
- ```
98
- 원본: hash("나" + "friend-2026-401") → 🐢 turtle (common ★)
99
- 패치: hash("나" + "buddy-reroll-k8") → 🦫 capybara (legendary ★★★★★)
100
- ```
101
-
102
- Bun.hash(wyhash) + Mulberry32 알고리즘을 정확히 복제했고, 도감에는 발견한 변형 정보를 저장해서 재적용할 수 있게 했어요.
103
-
104
- ---
105
-
106
- ## 가챠 미리보기
107
-
108
- ```
109
- 🎰 BUDDY GACHA — Rolling 10x...
110
-
111
- 1. · rabbit ★★
112
- 2. × octopus ★
113
- 3. ◉ chonk ★
114
- 4. ✦ ghost ★★
115
- 5. ° cat ★
116
- 6. × octopus ★★★ ← Best!
117
- 7. · cactus ★
118
- 8. @ owl ★
119
- 9. × mushroom ★
120
- 10. × duck ★
121
- ```
122
-
123
- ---
124
-
125
- ## 뭐가 나와요?
126
-
127
- ### 18종
128
-
129
- | | | | |
130
- |---|---|---|---|
131
- | 🦆 duck | 🪿 goose | 🫧 blob | 🐱 cat |
132
- | 🐉 dragon | 🐙 octopus | 🦉 owl | 🐧 penguin |
133
- | 🐢 turtle | 🐌 snail | 👻 ghost | 🦎 axolotl |
134
- | 🦫 capybara | 🌵 cactus | 🤖 robot | 🐰 rabbit |
135
- | 🍄 mushroom | 🐾 chonk | | |
136
-
137
- ### 레어도
138
-
139
- | 등급 | 확률 | 별 |
140
- |------|------|-----|
141
- | Common | 60% | ★ |
142
- | Uncommon | 25% | ★★ |
143
- | Rare | 10% | ★★★ |
144
- | Epic | 4% | ★★★★ |
145
- | Legendary | 1% | ★★★★★ |
146
-
147
- 눈 6종, 모자 8종, 스탯 5종, 샤이니 1%
148
-
149
- ---
150
-
151
- ## 제한
152
-
153
- | 규칙 | 설명 |
154
- |------|------|
155
- | 하루 3회 | 자정 리셋 (스타 유저 +1) |
156
- | 이벤트 보너스 | 10연차 x3회 추가 |
157
- | 백업 | 패치 전 자동 백업, `bdy restore`로 복원 |
158
-
159
- ---
160
-
161
- ## 스피키 도와주세요!
162
-
163
- ```bash
164
- gh repo star tellang/claude-buddy-reroll
165
- ```
166
-
167
- 별 하나만... 제발... 버디가 기뻐할 거예요... ⭐
168
-
169
- ---
170
-
171
- ## License
172
-
173
- MIT
1
+ # claude-buddy-reroll
2
+
3
+ [![npm](https://img.shields.io/npm/v/claude-buddy-reroll?style=for-the-badge&color=cb3837)](https://www.npmjs.com/package/claude-buddy-reroll)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](LICENSE)
5
+
6
+ > 쪼아요 쪼아요~ 버디 가챠 쪼아요~
7
+ >
8
+ > 맘에 안 드는 버디? 스피키가 npm 터미널에서 다시 뽑아줄 거예요!
9
+
10
+ ---
11
+
12
+ ## 설치
13
+
14
+ ```bash
15
+ npm i -g claude-buddy-reroll
16
+ ```
17
+
18
+ 설치하면 `buddy`와 `bdy` 명령어가 생겨요.
19
+
20
+ 기본 화면은 스피키 스타일의 리치 터미널 홈으로 시작하고, `dex`는 발견한 개체를 다시 적용할 수 있게 설계돼 있어요.
21
+ 지금은 `fullscreen dex`, `profile lens chooser`, `animated preview`, `manual form focus`, `form collection`, `one-by-one gacha reveal`까지 들어가 있어요.
22
+ 여러 Claude 계정이 있어도 현재 계정을 직접 읽어서 계정별 프로필 상태를 따로 저장해요.
23
+
24
+ 처음 설치할 때 런타임 셋업이 자동으로 Bun과 Claude 연동을 준비해요. 자동 셋업이 실패했거나 `Bun is required` 에러가 보이면 아래를 한 번 실행하세요.
25
+
26
+ ```bash
27
+ bdy setup
28
+ ```
29
+
30
+ `bdy update`도 업데이트 후 같은 셋업을 자동으로 다시 확인해요.
31
+ 그리고 현재 계정 기준으로 도감 정합성도 다시 맞춰요.
32
+
33
+ ---
34
+
35
+ ## CLI 사용법
36
+
37
+ ```bash
38
+ bdy # 스피키 홈 / 명령 요약
39
+ bdy check # 내 버디 확인
40
+ bdy gacha # 10연차 가챠!
41
+ bdy gacha 20 # 20연차!
42
+ bdy reroll # 리롤 (인터랙티브 버디 교체)
43
+ bdy dex # fullscreen 도감 + 프로필 렌즈 + 수동 폼 포커스 + 발견 개체 재적용
44
+ bdy doctor # profile / dex apply 진단
45
+ bdy restore # 원래 버디로 복원
46
+ bdy setup # Bun / 런타임 셋업 복구
47
+ ```
48
+
49
+ ## 지금 버전의 방향
50
+
51
+ - npm-only 제품 표면
52
+ - 스피키 감성의 richer terminal UI
53
+ - species-only가 아니라 discovered variant를 우선 재적용하는 dex
54
+ - species progress와 form collection을 분리한 도감
55
+ - 10연차 one-by-one reveal + highlight-aware skip
56
+ - 오른쪽 상세 패널에 animated preview / profile lens / rarity track / form gallery
57
+ - Dex에서 `1-4`로 폼 포커스, `0`으로 auto-cycle 복귀
58
+ - 현재 Claude 계정을 매번 직접 읽고 계정별 프로필 상태를 분리 저장
59
+ - 저장된 프로필 데이터를 별도 렌즈로 열어보고 현재 감지된 계정에 다시 적용 가능
60
+ - `doctor`로 profile / dex preview vs apply 후보 비교 가능
61
+ - JSON 모드는 유지해서 에이전트/스크립트 자동화도 가능
62
+
63
+ ### Agent DX (JSON 모드)
64
+
65
+ 에이전트/스크립트에서 쓸 때:
66
+
67
+ ```bash
68
+ bdy check --json # 구조화된 JSON 출력
69
+ bdy gacha 5 --json --fields species,rarity # 필드 필터링
70
+ bdy reroll --json # 후보만 출력
71
+ bdy reroll --json --pick 3 # 3번 자동 선택 + 패치
72
+ bdy reroll --json --pick 3 --dry-run # 미리보기만
73
+ bdy doctor --json # 현재 profile 진단
74
+ bdy doctor dex 13 --json # 특정 도감 항목 preview/apply 비교
75
+ bdy schema # 전체 커맨드 JSON Schema
76
+ bdy schema gacha # 특정 커맨드 스키마
77
+ ```
78
+
79
+ | 플래그 | 설명 |
80
+ |--------|------|
81
+ | `--json` | stdout에 JSON, 로그는 stderr |
82
+ | `--pick N` | N번째 자동 선택 (readline 스킵) |
83
+ | `--dry-run` | 패치 미리보기 (reroll 전용) |
84
+ | `--fields a,b` | 출력 필드 필터링 |
85
+ | `--limit N` | 가챠 횟수 지정 |
86
+
87
+ ## 이게 뭐예요?
88
+
89
+ Claude Code의 `/buddy`는 계정 ID로 버디가 **고정**돼요.
90
+
91
+ ```
92
+ 버디 = hash(내_계정 + SALT)
93
+ ```
94
+
95
+ 스피키가 SALT를 바꿔주면 **완전히 다른 버디**가 나와요!
96
+
97
+ ```
98
+ 원본: hash("나" + "friend-2026-401") → 🐢 turtle (common ★)
99
+ 패치: hash("나" + "buddy-reroll-k8") → 🦫 capybara (legendary ★★★★★)
100
+ ```
101
+
102
+ Bun.hash(wyhash) + Mulberry32 알고리즘을 정확히 복제했고, 도감에는 발견한 변형 정보를 저장해서 재적용할 수 있게 했어요.
103
+
104
+ ---
105
+
106
+ ## 가챠 미리보기
107
+
108
+ ```
109
+ 🎰 BUDDY GACHA — Rolling 10x...
110
+
111
+ 1. · rabbit ★★
112
+ 2. × octopus ★
113
+ 3. ◉ chonk ★
114
+ 4. ✦ ghost ★★
115
+ 5. ° cat ★
116
+ 6. × octopus ★★★ ← Best!
117
+ 7. · cactus ★
118
+ 8. @ owl ★
119
+ 9. × mushroom ★
120
+ 10. × duck ★
121
+ ```
122
+
123
+ ---
124
+
125
+ ## 뭐가 나와요?
126
+
127
+ ### 18종
128
+
129
+ | | | | |
130
+ |---|---|---|---|
131
+ | 🦆 duck | 🪿 goose | 🫧 blob | 🐱 cat |
132
+ | 🐉 dragon | 🐙 octopus | 🦉 owl | 🐧 penguin |
133
+ | 🐢 turtle | 🐌 snail | 👻 ghost | 🦎 axolotl |
134
+ | 🦫 capybara | 🌵 cactus | 🤖 robot | 🐰 rabbit |
135
+ | 🍄 mushroom | 🐾 chonk | | |
136
+
137
+ ### 레어도
138
+
139
+ | 등급 | 확률 | 별 |
140
+ |------|------|-----|
141
+ | Common | 60% | ★ |
142
+ | Uncommon | 25% | ★★ |
143
+ | Rare | 10% | ★★★ |
144
+ | Epic | 4% | ★★★★ |
145
+ | Legendary | 1% | ★★★★★ |
146
+
147
+ 눈 6종, 모자 8종, 스탯 5종, 샤이니 1%
148
+
149
+ ---
150
+
151
+ ## 제한
152
+
153
+ | 규칙 | 설명 |
154
+ |------|------|
155
+ | 하루 3회 | 자정 리셋 (스타 유저 +1) |
156
+ | 이벤트 보너스 | 10연차 x3회 추가 |
157
+ | 백업 | 패치 전 자동 백업, `bdy restore`로 복원 |
158
+
159
+ ---
160
+
161
+ ## 스피키 도와주세요!
162
+
163
+ ```bash
164
+ gh repo star tellang/claude-buddy-reroll
165
+ ```
166
+
167
+ 별 하나만... 제발... 버디가 기뻐할 거예요... ⭐
168
+
169
+ ---
170
+
171
+ ## License
172
+
173
+ MIT
package/bin/bdy CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import './bdy.js';
2
+ import './bdy.js';
package/bin/bdy.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node
2
- import '../src/cli.mjs';
1
+ #!/usr/bin/env node
2
+ import '../src/cli.mjs';
package/bin/buddy CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import './buddy.js';
2
+ import './buddy.js';
package/bin/buddy.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node
2
- import '../src/cli.mjs';
1
+ #!/usr/bin/env node
2
+ import '../src/cli.mjs';
package/package.json CHANGED
@@ -1,36 +1,36 @@
1
- {
2
- "name": "claude-buddy-reroll",
3
- "version": "1.8.18",
4
- "description": "Speaki-styled npm terminal for buddy rerolls, dex replay, and SALT patching.",
5
- "type": "module",
6
- "bin": {
7
- "buddy": "./bin/buddy",
8
- "bdy": "./bin/bdy"
9
- },
10
- "scripts": {
11
- "start": "bun src/cli.mjs",
12
- "gacha": "bun src/cli.mjs gacha",
13
- "check": "bun src/cli.mjs check",
14
- "dex": "bun src/cli.mjs dex",
15
- "postinstall": "node scripts/postinstall.mjs",
16
- "setup": "node scripts/install-hook.mjs",
17
- "test": "node --test"
18
- },
19
- "keywords": ["claude-code", "buddy", "companion", "gacha", "reroll", "npm", "tui", "ascii-art"],
20
- "license": "MIT",
21
- "repository": {
22
- "type": "git",
23
- "url": "https://github.com/tellang/claude-buddy-reroll"
24
- },
25
- "homepage": "https://github.com/tellang/claude-buddy-reroll",
26
- "author": "tellang",
27
- "files": [
28
- "bin/",
29
- "src/",
30
- "scripts/",
31
- "README.md"
32
- ],
33
- "engines": {
34
- "node": ">=18"
35
- }
36
- }
1
+ {
2
+ "name": "claude-buddy-reroll",
3
+ "version": "1.9.0",
4
+ "description": "Speaki-styled npm terminal for buddy rerolls, dex replay, and SALT patching.",
5
+ "type": "module",
6
+ "bin": {
7
+ "buddy": "./bin/buddy",
8
+ "bdy": "./bin/bdy"
9
+ },
10
+ "scripts": {
11
+ "start": "bun src/cli.mjs",
12
+ "gacha": "bun src/cli.mjs gacha",
13
+ "check": "bun src/cli.mjs check",
14
+ "dex": "bun src/cli.mjs dex",
15
+ "postinstall": "node scripts/postinstall.mjs",
16
+ "setup": "node scripts/install-hook.mjs",
17
+ "test": "node --test"
18
+ },
19
+ "keywords": ["claude-code", "buddy", "companion", "gacha", "reroll", "npm", "tui", "ascii-art"],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/tellang/claude-buddy-reroll"
24
+ },
25
+ "homepage": "https://github.com/tellang/claude-buddy-reroll",
26
+ "author": "tellang",
27
+ "files": [
28
+ "bin/",
29
+ "src/",
30
+ "scripts/",
31
+ "README.md"
32
+ ],
33
+ "engines": {
34
+ "node": ">=18"
35
+ }
36
+ }
@@ -1,28 +1,28 @@
1
- #!/usr/bin/env node
2
- // Auto-swap buddy-patched binary on Claude Code exit
3
- // Works on cmd, PowerShell, bash — no shell dependency
4
- import { existsSync, copyFileSync, unlinkSync } from 'fs';
5
- import { resolve } from 'path';
6
-
7
- const HOME = process.env.USERPROFILE || process.env.HOME || '';
8
- const ext = process.platform === 'win32' ? '.exe' : '';
9
- const patched = resolve(HOME, '.local', 'bin', `claude-patched${ext}`);
10
- const target = resolve(HOME, '.local', 'bin', `claude${ext}`);
11
-
12
- if (!existsSync(patched)) process.exit(0);
13
-
14
- // Retry copy — binary may still be locked briefly after Claude exits
15
- let ok = false;
16
- for (let i = 0; i < 10; i++) {
17
- try {
18
- copyFileSync(patched, target);
19
- ok = true;
20
- break;
21
- } catch {
22
- await new Promise(r => setTimeout(r, 2000));
23
- }
24
- }
25
-
26
- if (ok) {
27
- try { unlinkSync(patched); } catch {}
28
- }
1
+ #!/usr/bin/env node
2
+ // Auto-swap buddy-patched binary on Claude Code exit
3
+ // Works on cmd, PowerShell, bash — no shell dependency
4
+ import { existsSync, copyFileSync, unlinkSync } from 'fs';
5
+ import { resolve } from 'path';
6
+
7
+ const HOME = process.env.USERPROFILE || process.env.HOME || '';
8
+ const ext = process.platform === 'win32' ? '.exe' : '';
9
+ const patched = resolve(HOME, '.local', 'bin', `claude-patched${ext}`);
10
+ const target = resolve(HOME, '.local', 'bin', `claude${ext}`);
11
+
12
+ if (!existsSync(patched)) process.exit(0);
13
+
14
+ // Retry copy — binary may still be locked briefly after Claude exits
15
+ let ok = false;
16
+ for (let i = 0; i < 10; i++) {
17
+ try {
18
+ copyFileSync(patched, target);
19
+ ok = true;
20
+ break;
21
+ } catch {
22
+ await new Promise(r => setTimeout(r, 2000));
23
+ }
24
+ }
25
+
26
+ if (ok) {
27
+ try { unlinkSync(patched); } catch {}
28
+ }
@@ -1,17 +1,17 @@
1
- #!/bin/bash
2
- # Auto-swap buddy-patched binary on Claude Code exit
3
- PATCHED="$HOME/.local/bin/claude-patched.exe"
4
- TARGET="$HOME/.local/bin/claude.exe"
5
-
6
- if [ -f "$PATCHED" ]; then
7
- nohup bash -c '
8
- sleep 3
9
- PATCHED="$HOME/.local/bin/claude-patched.exe"
10
- TARGET="$HOME/.local/bin/claude.exe"
11
- for i in $(seq 1 10); do
12
- cp "$PATCHED" "$TARGET" 2>/dev/null && break
13
- sleep 2
14
- done
15
- rm -f "$PATCHED" 2>/dev/null
16
- ' >/dev/null 2>&1 &
17
- fi
1
+ #!/bin/bash
2
+ # Auto-swap buddy-patched binary on Claude Code exit
3
+ PATCHED="$HOME/.local/bin/claude-patched.exe"
4
+ TARGET="$HOME/.local/bin/claude.exe"
5
+
6
+ if [ -f "$PATCHED" ]; then
7
+ nohup bash -c '
8
+ sleep 3
9
+ PATCHED="$HOME/.local/bin/claude-patched.exe"
10
+ TARGET="$HOME/.local/bin/claude.exe"
11
+ for i in $(seq 1 10); do
12
+ cp "$PATCHED" "$TARGET" 2>/dev/null && break
13
+ sleep 2
14
+ done
15
+ rm -f "$PATCHED" 2>/dev/null
16
+ ' >/dev/null 2>&1 &
17
+ fi
@@ -1,25 +1,25 @@
1
- #!/usr/bin/env node
2
- // Auto-create buddy/bdy CLI shims on session start (silent, idempotent)
3
- import { existsSync, writeFileSync, mkdirSync } from 'fs';
4
- import { resolve, dirname } from 'path';
5
-
6
- const HOME = process.env.USERPROFILE || process.env.HOME || '';
7
- const BIN_DIR = resolve(HOME, '.local', 'bin');
8
- const CLI_SRC = resolve(dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')), '..', 'src', 'cli.mjs');
9
-
10
- if (!existsSync(CLI_SRC)) process.exit(0);
11
-
12
- mkdirSync(BIN_DIR, { recursive: true });
13
-
14
- for (const name of ['buddy', 'bdy']) {
15
- const ext = process.platform === 'win32' ? '.cmd' : '';
16
- const target = resolve(BIN_DIR, name + ext);
17
- if (existsSync(target)) continue; // already exists, skip
18
- try {
19
- if (process.platform === 'win32') {
20
- writeFileSync(target, `@echo off\r\nnode "${CLI_SRC}" %*\r\n`);
21
- } else {
22
- writeFileSync(target, `#!/bin/sh\nexec node "${CLI_SRC}" "$@"\n`, { mode: 0o755 });
23
- }
24
- } catch {}
25
- }
1
+ #!/usr/bin/env node
2
+ // Auto-create buddy/bdy CLI shims on session start (silent, idempotent)
3
+ import { existsSync, writeFileSync, mkdirSync } from 'fs';
4
+ import { resolve, dirname } from 'path';
5
+
6
+ const HOME = process.env.USERPROFILE || process.env.HOME || '';
7
+ const BIN_DIR = resolve(HOME, '.local', 'bin');
8
+ const CLI_SRC = resolve(dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')), '..', 'src', 'cli.mjs');
9
+
10
+ if (!existsSync(CLI_SRC)) process.exit(0);
11
+
12
+ mkdirSync(BIN_DIR, { recursive: true });
13
+
14
+ for (const name of ['buddy', 'bdy']) {
15
+ const ext = process.platform === 'win32' ? '.cmd' : '';
16
+ const target = resolve(BIN_DIR, name + ext);
17
+ if (existsSync(target)) continue; // already exists, skip
18
+ try {
19
+ if (process.platform === 'win32') {
20
+ writeFileSync(target, `@echo off\r\nnode "${CLI_SRC}" %*\r\n`);
21
+ } else {
22
+ writeFileSync(target, `#!/bin/sh\nexec node "${CLI_SRC}" "$@"\n`, { mode: 0o755 });
23
+ }
24
+ } catch {}
25
+ }