honeytree 1.1.5 → 1.2.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/src/scanner.js ADDED
@@ -0,0 +1,102 @@
1
+ // src/scanner.js — walks project directory to collect file metadata
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execSync } from "node:child_process";
5
+
6
+ const IGNORE_DIRS = new Set([
7
+ "node_modules", ".git", ".hg", ".svn", "dist", "build",
8
+ ".next", "__pycache__", ".venv", "venv", "coverage",
9
+ ".superpowers", ".honeytree",
10
+ ]);
11
+
12
+ const BINARY_EXTENSIONS = new Set([
13
+ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".webp",
14
+ ".mp3", ".mp4", ".wav", ".avi", ".mov", ".mkv",
15
+ ".zip", ".tar", ".gz", ".bz2", ".7z", ".rar",
16
+ ".exe", ".dll", ".so", ".dylib", ".o", ".a",
17
+ ".woff", ".woff2", ".ttf", ".eot", ".otf",
18
+ ".pdf", ".doc", ".docx", ".xls", ".xlsx",
19
+ ".lock", ".sqlite", ".db",
20
+ ]);
21
+
22
+ export function scanDirectory(rootDir) {
23
+ const files = [];
24
+
25
+ function walk(dir, relativeBase) {
26
+ let entries;
27
+ try {
28
+ entries = fs.readdirSync(dir, { withFileTypes: true });
29
+ } catch {
30
+ return;
31
+ }
32
+
33
+ for (const entry of entries) {
34
+ if (entry.name.startsWith(".") && entry.name !== ".") {
35
+ if (entry.isDirectory()) continue;
36
+ }
37
+
38
+ const fullPath = path.join(dir, entry.name);
39
+ const relativePath = relativeBase
40
+ ? `${relativeBase}/${entry.name}`
41
+ : entry.name;
42
+
43
+ if (entry.isDirectory()) {
44
+ if (IGNORE_DIRS.has(entry.name)) continue;
45
+ walk(fullPath, relativePath);
46
+ } else if (entry.isFile()) {
47
+ const ext = path.extname(entry.name).toLowerCase();
48
+ if (BINARY_EXTENSIONS.has(ext)) continue;
49
+
50
+ let stat;
51
+ try {
52
+ stat = fs.statSync(fullPath);
53
+ } catch {
54
+ continue;
55
+ }
56
+
57
+ files.push({
58
+ absolutePath: fullPath,
59
+ relativePath,
60
+ extension: ext,
61
+ size: stat.size,
62
+ directory: relativeBase || ".",
63
+ });
64
+ }
65
+ }
66
+ }
67
+
68
+ walk(rootDir, "");
69
+ return files;
70
+ }
71
+
72
+ export function getGitChurn(rootDir) {
73
+ try {
74
+ const output = execSync("git log --format= --name-only --diff-filter=ACMR", {
75
+ cwd: rootDir,
76
+ encoding: "utf-8",
77
+ timeout: 10000,
78
+ stdio: ["pipe", "pipe", "pipe"],
79
+ });
80
+
81
+ const counts = {};
82
+ for (const line of output.split("\n")) {
83
+ const trimmed = line.trim();
84
+ if (!trimmed) continue;
85
+ counts[trimmed] = (counts[trimmed] || 0) + 1;
86
+ }
87
+ return counts;
88
+ } catch {
89
+ return {};
90
+ }
91
+ }
92
+
93
+ export function scanCodebase(rootDir) {
94
+ const files = scanDirectory(rootDir);
95
+ const churn = getGitChurn(rootDir);
96
+
97
+ for (const file of files) {
98
+ file.churn = churn[file.relativePath] || 0;
99
+ }
100
+
101
+ return files;
102
+ }
package/src/sprites.js CHANGED
@@ -14,7 +14,20 @@ const COLORS = {
14
14
  cherryBloom: "#f0b7cf",
15
15
  };
16
16
 
17
- function parse(template, palette) {
17
+ const DETAIL_COLORS = {
18
+ mushroom: "#c4a882",
19
+ mushroomCap: "#9e4a3a",
20
+ rock: "#6b6b6b",
21
+ rockLight: "#8a8a8a",
22
+ grass: "#4a7a3a",
23
+ grassLight: "#6ba85a",
24
+ leaf: "#8a6a3a",
25
+ leafDark: "#6a4a2a",
26
+ bush: "#3a6a2a",
27
+ bushLight: "#5a8a4a",
28
+ };
29
+
30
+ export function parse(template, palette) {
18
31
  const lines = template.trim().split("\n");
19
32
  const width = Math.max(...lines.map((line) => line.length));
20
33
  const rows = lines
@@ -229,6 +242,50 @@ pPPpPPPp
229
242
  },
230
243
  };
231
244
 
245
+ const GROUND_DETAILS = {
246
+ mushroom: parse(
247
+ `
248
+ rr
249
+ t
250
+ `,
251
+ { r: DETAIL_COLORS.mushroomCap, t: DETAIL_COLORS.mushroom },
252
+ ),
253
+ rock: parse(
254
+ `
255
+ rR
256
+ `,
257
+ { r: DETAIL_COLORS.rock, R: DETAIL_COLORS.rockLight },
258
+ ),
259
+ grass: parse(
260
+ `
261
+ gG
262
+ `,
263
+ { g: DETAIL_COLORS.grass, G: DETAIL_COLORS.grassLight },
264
+ ),
265
+ leaf: parse(
266
+ `
267
+ lL
268
+ `,
269
+ { l: DETAIL_COLORS.leaf, L: DETAIL_COLORS.leafDark },
270
+ ),
271
+ bush: parse(
272
+ `
273
+ bB
274
+ `,
275
+ { b: DETAIL_COLORS.bush, B: DETAIL_COLORS.bushLight },
276
+ ),
277
+ };
278
+
279
+ export const GROUND_DETAIL_TYPES = Object.keys(GROUND_DETAILS);
280
+
281
+ export function getGroundDetail(type) {
282
+ const detail = GROUND_DETAILS[type];
283
+ if (!detail) {
284
+ throw new Error(`Unknown ground detail type: ${type}`);
285
+ }
286
+ return detail;
287
+ }
288
+
232
289
  function getGrowthStage(growth) {
233
290
  if (growth < 0.2) return "seed";
234
291
  if (growth < 0.5) return "sapling";
@@ -243,3 +300,4 @@ export function getSprite(type, growth) {
243
300
  }
244
301
  return spriteSet[getGrowthStage(growth)];
245
302
  }
303
+