honeytree 1.0.1 → 1.0.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/README.md +52 -25
- package/package.json +1 -1
- package/src/viewer.js +69 -16
package/README.md
CHANGED
|
@@ -1,44 +1,49 @@
|
|
|
1
1
|
# Honeytree
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/honeytree)
|
|
4
|
+
[](https://github.com/Varun2009178/honeytree/blob/main/LICENSE)
|
|
5
|
+
|
|
3
6
|
Grow a pixel-art forest in your terminal every time you use Claude Code.
|
|
4
7
|
|
|
5
8
|
Each prompt plants a new tree. Each tree grows over time. Your forest evolves from a quiet clearing into an ancient woodland — and it never resets.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
8
13
|
|
|
9
14
|
```bash
|
|
10
15
|
npm install -g honeytree
|
|
16
|
+
honeytree init
|
|
17
|
+
honeytree
|
|
11
18
|
```
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
That's it. Three commands:
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
1. **Install** the CLI globally
|
|
23
|
+
2. **Init** creates your forest file and registers a Claude Code hook
|
|
24
|
+
3. **Run the viewer** in a separate terminal to watch your forest grow
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
After setup, trees are planted automatically after every Claude Code response. No manual steps needed.
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
---
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
honeytree
|
|
25
|
-
```
|
|
30
|
+
## How It Works
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
When you run `honeytree init`, it does two things:
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
- Creates `~/.honeydew/forest.json` to store your forest state
|
|
35
|
+
- Adds a `Stop` hook to `~/.claude/settings.json` that runs after every Claude Code response
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
2. **`honeytree`** — Opens the viewer that renders your forest in real time
|
|
37
|
+
From then on, every time Claude Code responds to a prompt, a new tree is planted in your forest automatically. Open the viewer in a second terminal to watch them grow in real time.
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
---
|
|
35
40
|
|
|
36
41
|
## Biomes
|
|
37
42
|
|
|
38
|
-
Your forest evolves as it grows:
|
|
43
|
+
Your forest evolves visually as it grows — the sky, ground, and atmosphere all change:
|
|
39
44
|
|
|
40
45
|
| Trees | Biome | What changes |
|
|
41
|
-
|
|
46
|
+
|------:|-------|-------------|
|
|
42
47
|
| 0–9 | Clearing | Sparse stars, light ground |
|
|
43
48
|
| 10–24 | Grove | More stars, richer ground |
|
|
44
49
|
| 25–49 | Woodland | Dense canopy, varied starlight |
|
|
@@ -47,20 +52,42 @@ Your forest evolves as it grows:
|
|
|
47
52
|
|
|
48
53
|
Trees are never deleted. The forest only grows.
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
---
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
- **Pine** — Tall, triangular shape
|
|
54
|
-
- **Birch** — Light trunk, bright leaves
|
|
55
|
-
- **Willow** — Drooping canopy
|
|
56
|
-
- **Cherry** — Pink blossoms
|
|
57
|
+
## Tree Species
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
Five species are randomly assigned when a tree is planted:
|
|
60
|
+
|
|
61
|
+
| Species | Look |
|
|
62
|
+
|---------|------|
|
|
63
|
+
| Oak | Wide, rounded canopy |
|
|
64
|
+
| Pine | Tall, triangular shape |
|
|
65
|
+
| Birch | Light trunk, bright leaves |
|
|
66
|
+
| Willow | Drooping canopy |
|
|
67
|
+
| Cherry | Pink blossoms |
|
|
68
|
+
|
|
69
|
+
Each species has 4 growth stages (seed, sapling, young, full). Existing trees grow a little with each new prompt.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Viewer
|
|
74
|
+
|
|
75
|
+
The viewer adapts to your terminal width — expand your terminal and new trees will spread across the full width.
|
|
76
|
+
|
|
77
|
+
Press `Ctrl+C` to exit. The viewer shows a summary of your forest when you close it.
|
|
78
|
+
|
|
79
|
+
---
|
|
59
80
|
|
|
60
81
|
## Requirements
|
|
61
82
|
|
|
62
83
|
- Node.js 18+
|
|
63
|
-
- Claude Code (for the automatic hook)
|
|
84
|
+
- [Claude Code](https://claude.com/claude-code) (for the automatic hook)
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- **npm**: [npmjs.com/package/honeytree](https://www.npmjs.com/package/honeytree)
|
|
89
|
+
- **GitHub**: [github.com/Varun2009178/honeytree](https://github.com/Varun2009178/honeytree)
|
|
90
|
+
- **Issues**: [github.com/Varun2009178/honeytree/issues](https://github.com/Varun2009178/honeytree/issues)
|
|
64
91
|
|
|
65
92
|
## License
|
|
66
93
|
|
package/package.json
CHANGED
package/src/viewer.js
CHANGED
|
@@ -64,10 +64,12 @@ export async function viewer() {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Save terminal width so plant knows how wide to spread trees
|
|
67
|
+
let ignoreNextChange = false;
|
|
67
68
|
function syncWidth() {
|
|
68
69
|
const cols = process.stdout.columns || 80;
|
|
69
70
|
if (forest.viewerWidth !== cols) {
|
|
70
71
|
forest.viewerWidth = cols;
|
|
72
|
+
ignoreNextChange = true;
|
|
71
73
|
writeForest(forest);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -78,6 +80,8 @@ export async function viewer() {
|
|
|
78
80
|
renderForest(forest);
|
|
79
81
|
|
|
80
82
|
let lastMaxId = forest.trees.reduce((max, tree) => Math.max(max, tree.id), 0);
|
|
83
|
+
let lastTotalPrompts = forest.totalPrompts;
|
|
84
|
+
let animating = false;
|
|
81
85
|
|
|
82
86
|
const cleanup = () => {
|
|
83
87
|
showCursor();
|
|
@@ -96,21 +100,70 @@ export async function viewer() {
|
|
|
96
100
|
renderForest(forest);
|
|
97
101
|
});
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
// Check for changes — used by both fs.watch and polling fallback
|
|
104
|
+
async function checkForUpdates() {
|
|
105
|
+
if (animating) return;
|
|
106
|
+
|
|
107
|
+
if (ignoreNextChange) {
|
|
108
|
+
ignoreNextChange = false;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const updated = readForest();
|
|
113
|
+
if (!updated) return;
|
|
114
|
+
|
|
115
|
+
// Only re-render if something actually changed
|
|
116
|
+
if (updated.totalPrompts === lastTotalPrompts) return;
|
|
117
|
+
|
|
118
|
+
const nextMaxId = updated.trees.reduce((max, tree) => Math.max(max, tree.id), 0);
|
|
119
|
+
forest = updated;
|
|
120
|
+
lastTotalPrompts = forest.totalPrompts;
|
|
121
|
+
|
|
122
|
+
if (nextMaxId > lastMaxId) {
|
|
123
|
+
lastMaxId = nextMaxId;
|
|
124
|
+
animating = true;
|
|
125
|
+
await animateNewTree(forest, nextMaxId);
|
|
126
|
+
animating = false;
|
|
127
|
+
} else {
|
|
128
|
+
renderForest(forest);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// fs.watch can drop events on macOS after atomic renames, so
|
|
133
|
+
// use it for fast response but also poll as a reliable fallback
|
|
134
|
+
function startWatcher() {
|
|
135
|
+
try {
|
|
136
|
+
const watcher = fs.watch(forestFile, () => {
|
|
137
|
+
checkForUpdates();
|
|
138
|
+
});
|
|
139
|
+
watcher.on("error", () => {});
|
|
140
|
+
return watcher;
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let watcher = startWatcher();
|
|
147
|
+
|
|
148
|
+
// Poll every 800ms as fallback — cheap since it only reads if mtime changed
|
|
149
|
+
let lastMtime = 0;
|
|
150
|
+
try {
|
|
151
|
+
lastMtime = fs.statSync(forestFile).mtimeMs;
|
|
152
|
+
} catch {}
|
|
153
|
+
|
|
154
|
+
setInterval(() => {
|
|
155
|
+
try {
|
|
156
|
+
const mtime = fs.statSync(forestFile).mtimeMs;
|
|
157
|
+
if (mtime !== lastMtime) {
|
|
158
|
+
lastMtime = mtime;
|
|
159
|
+
checkForUpdates();
|
|
160
|
+
|
|
161
|
+
// Re-establish watcher in case rename killed it
|
|
162
|
+
if (watcher) {
|
|
163
|
+
try { watcher.close(); } catch {}
|
|
164
|
+
}
|
|
165
|
+
watcher = startWatcher();
|
|
113
166
|
}
|
|
114
|
-
}
|
|
115
|
-
});
|
|
167
|
+
} catch {}
|
|
168
|
+
}, 800);
|
|
116
169
|
}
|