bun-sticky 1.0.0 → 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.
@@ -0,0 +1,12 @@
1
+ # Score Current Project
2
+
3
+ Run bun-sticky on the current directory to check FAF score.
4
+
5
+ ```bash
6
+ bun run index.ts score
7
+ ```
8
+
9
+ Review the output:
10
+ - Check which slots are missing
11
+ - Suggest improvements to reach next tier
12
+ - Target: 85%+ Bronze for production-ready
@@ -0,0 +1,15 @@
1
+ # Run WJTTC Test Suite
2
+
3
+ Run the championship-grade test suite and report results.
4
+
5
+ ```bash
6
+ bun test --summary all
7
+ ```
8
+
9
+ Expected: All tests pass (200+)
10
+
11
+ If any tests fail:
12
+ 1. Identify the failing test
13
+ 2. Check the assertion message
14
+ 3. Fix the code or test
15
+ 4. Re-run until all green
@@ -2,4 +2,4 @@
2
2
  # Free for devs. Teams and enterprise contact us.
3
3
 
4
4
  github: wolfejam
5
- custom: ['https://faf.one/enterprise']
5
+ custom: ['mailto:team@faf.one']
package/CLAUDE.md CHANGED
@@ -1,81 +1,96 @@
1
- # Bun Sticky
1
+ # bun-sticky
2
2
 
3
- Fastest bun under the sum. Bun-native .faf CLI.
3
+ Fastest bun under the sum. Bun-native .faf CLI with Wolfejam slot-based scoring.
4
+
5
+ ## Quick Commands
4
6
 
5
- ## Quick Start
6
7
  ```bash
7
- bunx bun-sticky score # Score current project
8
- bunx bun-sticky init app # Create project.faf
9
- bunx bun-sticky help # Show commands
8
+ bun test # Run WJTTC test suite (200+ tests)
9
+ bun run index.ts score # Score current project
10
+ bun run index.ts help # Show commands
11
+ bun publish # Publish to npm (see PUBLISH-PROTOCOL.md)
10
12
  ```
11
13
 
12
14
  ## Architecture
15
+
13
16
  ```
14
17
  bun-sticky/
15
- ├── index.ts # CLI + ASCII banner
18
+ ├── index.ts # CLI entry + ASCII banner
16
19
  ├── lib/
17
- │ ├── parser.ts # Zero-dep YAML parser
18
- │ ├── scorer.ts # Wolfejam slot-based scoring
19
- │ └── tier.ts # 9-tier system
20
- ├── tests/
21
- │ └── sticky.test.ts # 177 tests, WJTTC championship
22
- └── package.json # Zero dependencies
20
+ │ ├── parser.ts # Zero-dep YAML parser
21
+ │ ├── scorer.ts # Wolfejam 21-slot scoring
22
+ │ └── tier.ts # 7-tier ranking system
23
+ └── tests/
24
+ ├── sticky.test.ts # Core unit tests
25
+ └── wjttc.test.ts # Championship test suite
23
26
  ```
24
27
 
25
- ## Core Concepts
28
+ ## Scoring System
29
+
30
+ **Wolfejam Slot-Based Scoring** (NOT Elon weights):
26
31
 
27
- **Wolfejam Slot-Based Scoring** (NOT Elon weights)
28
32
  - 21 total slots across 5 categories
29
- - Type-aware: CLI=9 slots, Fullstack=21 slots
30
- - Score = Filled Slots / Applicable Slots × 100
33
+ - Type-aware: CLI=9, Fullstack=21, etc.
34
+ - Formula: `Score = (Filled / Applicable) × 100`
35
+
36
+ | Category | Slots | Fields |
37
+ |----------|-------|--------|
38
+ | Project | 3 | name, goal, main_language |
39
+ | Frontend | 4 | frontend, css_framework, ui_library, state_management |
40
+ | Backend | 5 | backend, api_type, runtime, database, connection |
41
+ | Universal | 3 | hosting, build, cicd |
42
+ | Human | 6 | who, what, why, where, when, how |
43
+
44
+ ## Tier System
31
45
 
32
- **Tier System**
33
46
  | Score | Tier | Emoji |
34
47
  |-------|------|-------|
35
- | 100% | Trophy | 🏆 |
36
- | 99%+ | Gold | 🥇 |
37
- | 95%+ | Silver | 🥈 |
38
- | 85%+ | Bronze | 🥉 |
39
- | 70%+ | Green | 🟢 |
40
- | 55%+ | Yellow | 🟡 |
41
- | <55% | Red | 🔴 |
48
+ | 100% | Trophy | 🏆 |
49
+ | 99%+ | Gold | 🥇 |
50
+ | 95%+ | Silver | 🥈 |
51
+ | 85%+ | Bronze | 🥉 |
52
+ | 70%+ | Green | 🟢 |
53
+ | 55%+ | Yellow | 🟡 |
54
+ | <55% | Red | 🔴 |
42
55
 
43
56
  ## Key Files
44
- | File | Line | What |
45
- |------|------|------|
46
- | `lib/scorer.ts` | 16 | SLOTS definition (21 slots) |
47
- | `lib/scorer.ts` | 68 | TYPE_CATEGORIES mapping |
48
- | `lib/scorer.ts` | 173 | calculateScore() |
49
- | `lib/tier.ts` | 22 | getTier() |
50
- | `lib/parser.ts` | 1 | parseYaml() zero-dep |
51
-
52
- ## Design Principles
53
- 1. **Zero Dependencies** - Pure Bun APIs
54
- 2. **TypeScript Native** - No build step
55
- 3. **Speed First** - Sub-50ms cold start
56
- 4. **WJTTC Testing** - Championship-grade test suite
57
-
58
- ## Commands
59
- | Command | Description |
60
- |---------|-------------|
61
- | `score` | Show FAF score + tier |
62
- | `init <name>` | Create project.faf |
63
- | `sync` | Sync project.faf → CLAUDE.md |
64
- | `version` | Show version |
65
- | `help` | Show help |
57
+
58
+ | File | Purpose |
59
+ |------|---------|
60
+ | `lib/scorer.ts:16` | SLOTS definition (21 slots) |
61
+ | `lib/scorer.ts:68` | TYPE_CATEGORIES mapping |
62
+ | `lib/scorer.ts:173` | calculateScore() function |
63
+ | `lib/tier.ts:22` | getTier() function |
64
+ | `lib/parser.ts:1` | parseYaml() zero-dep parser |
66
65
 
67
66
  ## Testing
67
+
68
+ Championship-grade WJTTC test suite with full Bun test API coverage:
69
+
70
+ - `test.each` - Parametrized tests
71
+ - `test.concurrent` - Parallel execution
72
+ - `mock`, `spyOn` - Mocking
73
+ - Lifecycle hooks - beforeAll, afterEach, etc.
74
+ - Full matcher suite
75
+
68
76
  ```bash
69
- bun test
70
- # 177 tests, 1254 assertions
71
- # Full Bun API: test.each, mock, spyOn, snapshots
72
- # WJTTC Championship Grade
77
+ bun test --coverage # With coverage
78
+ bun test --watch # Watch mode
79
+ CLAUDECODE=1 bun test # AI-friendly output
73
80
  ```
74
81
 
75
- ## Part of FAF Ecosystem
76
- - **faf-cli** - Full Node.js CLI (15,000+ downloads)
77
- - **bun-sticky** - Bun-native lite CLI (you are here)
78
- - **xai-faf-zig** - Ultra-fast Zig implementation
82
+ ## Development Rules
83
+
84
+ 1. **Zero Dependencies** - Only Bun native APIs
85
+ 2. **No npm repeats** - Follow PUBLISH-PROTOCOL.md exactly
86
+ 3. **Tests first** - All changes need tests
87
+ 4. **Wolfejam slots only** - Never use Elon weights
88
+
89
+ ## Publishing
90
+
91
+ **NEVER publish without explicit GO! approval.**
92
+
93
+ See `PUBLISH-PROTOCOL.md` for the complete ceremony.
79
94
 
80
95
  ---
81
- *Zero dependencies. Pure Bun. Wolfejam slot-based scoring.*
96
+ *Part of FAF ecosystem. Built for Claude Code.*
@@ -3,10 +3,10 @@
3
3
  ## Pre-Publish Checklist
4
4
 
5
5
  ### 1. Code Quality
6
- - [ ] All 45 tests passing (`bun test`)
7
- - [ ] Zero TypeScript errors
8
- - [ ] Zero runtime dependencies
9
- - [ ] Pure Bun APIs only
6
+ - [x] All 177 tests passing (`bun test`)
7
+ - [x] Zero TypeScript errors
8
+ - [x] Zero runtime dependencies
9
+ - [x] Pure Bun APIs only
10
10
 
11
11
  ### 2. Version Bump
12
12
  ```bash
@@ -18,44 +18,42 @@
18
18
  ### 3. Test Suite
19
19
  ```bash
20
20
  bun test
21
- # Expect: 45 pass, 0 fail
21
+ # Expect: 177 pass, 0 fail
22
22
  ```
23
23
 
24
24
  ### 4. Manual Verification
25
25
  ```bash
26
- bun run index.ts --version
27
- bun run index.ts help
28
- bun run index.ts score
26
+ bunx bun-sticky --version # ✓
27
+ bunx bun-sticky help #
28
+ bunx bun-sticky init test #
29
+ bunx bun-sticky score # ✓
29
30
  ```
30
31
 
31
32
  ## Publish Commands
32
33
 
33
34
  ### npm Registry
34
35
  ```bash
35
- # Login (first time)
36
- npm login
37
-
38
- # Publish
39
- npm publish
36
+ npm publish --access public
40
37
  ```
41
38
 
42
- ### Bun Package Manager (future)
39
+ ### Verify
43
40
  ```bash
44
- # When Bun has its own registry
45
- bun publish
41
+ npm view bun-sticky
42
+ bunx bun-sticky help
46
43
  ```
47
44
 
48
45
  ## Post-Publish
49
46
 
50
- 1. Tag release in git
51
- 2. Update ZIG-n-RUST.md benchmarks
52
- 3. Announce in faf-cli ecosystem
47
+ 1. [x] Tag release in git
48
+ 2. [ ] Update ZIG-n-RUST.md benchmarks
49
+ 3. [ ] Announce in faf-cli ecosystem
53
50
 
54
51
  ## Version History
55
52
 
56
53
  | Version | Date | Notes |
57
54
  |---------|------|-------|
58
- | 1.0.0 | TBD | Initial release - Wolfejam slot-based scoring |
55
+ | 1.0.0 | 2024-12-22 | Initial release - Wolfejam slot-based scoring, 177 tests |
56
+ | 1.0.1 | 2024-12-22 | Proper publish ceremony, version consistency |
59
57
 
60
58
  ---
61
59
 
package/README.md CHANGED
@@ -18,7 +18,7 @@ bunx bun-sticky score
18
18
  ████ █▄▀ ▀▄▀ █ █
19
19
  ▀▀
20
20
 
21
- 🥐 Bun Sticky v1.0.0 .faf CLI
21
+ 🥐 Bun Sticky v1.0.1 .faf CLI
22
22
  Fastest bun under the sum.
23
23
 
24
24
  ────────────────────────────────────────────────
package/index.ts CHANGED
@@ -20,19 +20,9 @@ import { getTier } from "./lib/tier.ts";
20
20
  // CONSTANTS
21
21
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
22
 
23
- const VERSION = "1.0.0";
24
-
25
- // Bun Brand Colors (ANSI 256)
26
- const BUN_BLUE = "\x1b[38;5;39m"; // #00a6e1
27
- const BUN_PINK = "\x1b[38;5;205m"; // #ec4899
28
- const BUN_ORANGE = "\x1b[38;5;215m"; // #f89b4b (medium - croissant)
29
- const BUN_ORANGE_DARK = "\x1b[38;5;208m"; // dark orange (BUN)
30
- const BUN_YELLOW = "\x1b[38;5;220m"; // #febc2e
31
- const BUN_CREAM = "\x1b[38;5;223m"; // Croissant color
32
- const WHITE = "\x1b[97m"; // bright white (STICKY)
33
-
34
- // Standard colors
35
- const CYAN = "\x1b[36m";
23
+ const VERSION = "1.0.2";
24
+
25
+ // Standard colors only (B/W version - color reserved for ZIG poster child)
36
26
  const GREEN = "\x1b[32m";
37
27
  const YELLOW = "\x1b[33m";
38
28
  const RED = "\x1b[31m";
@@ -45,21 +35,21 @@ const RESET = "\x1b[0m";
45
35
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
46
36
 
47
37
  const BANNER = `
48
- ${BUN_BLUE}────────────────────────────────────────────────${RESET}
38
+ ────────────────────────────────────────────────
49
39
 
50
- ${BUN_ORANGE}▄▄${RESET} ${WHITE}▄▀▀▀ ▀█▀ █ ▄▀▀ █▄▀ █ █${RESET}
51
- ${BUN_ORANGE}████${RESET} ${WHITE}▀▀█▄ █ █ █ █▀▄ █${RESET}
52
- ${BUN_ORANGE}██████${RESET} ${WHITE}▄▄▄▀ █ █ ▀▀▀ █ █ █${RESET}
53
- ${BUN_ORANGE}████████${RESET}
54
- ${BUN_ORANGE}████████${RESET} ${BUN_ORANGE_DARK}█▀▄ █ █ █▀▄${RESET}
55
- ${BUN_ORANGE}██████${RESET} ${BUN_ORANGE_DARK}██▀ █ █ █ █${RESET}
56
- ${BUN_ORANGE}████${RESET} ${BUN_ORANGE_DARK}█▄▀ ▀▄▀ █ █${RESET}
57
- ${BUN_ORANGE}▀▀${RESET}
40
+ ▄▄ ▄▀▀▀ ▀█▀ █ ▄▀▀ █▄▀ █
41
+ ████ ▀▀█▄ █ █ █ █▀▄
42
+ ██████ ▄▄▄▀ █ █ ▀▀▀ █ █
43
+ ████████
44
+ ████████ █▀▄ █ █ █▀▄
45
+ ██████ ██▀ █ █ █
46
+ ████ █▄▀ ▀▄▀ █
47
+ ▀▀
58
48
 
59
- ${WHITE}🥐 Bun Sticky v${VERSION} .faf CLI${RESET}
60
- ${BUN_ORANGE_DARK}Fastest bun under the sum.${RESET}
49
+ 🥐 Bun Sticky v${VERSION} .faf CLI
50
+ Fastest bun under the sum.
61
51
 
62
- ${BUN_BLUE}────────────────────────────────────────────────${RESET}
52
+ ────────────────────────────────────────────────
63
53
  `;
64
54
 
65
55
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -112,6 +102,72 @@ async function cmdScore(): Promise<void> {
112
102
  console.log(` ${tier.color}${tier.emoji} ${BOLD}${result.score}%${RESET} ${tier.color}${tier.name}${RESET}`);
113
103
  console.log(` ${DIM}Filled: ${result.filled}/${result.total} slots${RESET}`);
114
104
  console.log();
105
+
106
+ // Show missing slots with copy-paste YAML
107
+ if (result.missing.length > 0) {
108
+ console.log(` ${YELLOW}Add to project.faf:${RESET}`);
109
+ console.log();
110
+
111
+ // Group by section
112
+ const projectMissing = result.missing.filter(s => s.startsWith("project."));
113
+ const humanMissing = result.missing.filter(s => s.startsWith("human_context."));
114
+ const stackMissing = result.missing.filter(s => s.startsWith("stack."));
115
+
116
+ if (projectMissing.length > 0) {
117
+ console.log(` ${DIM}project:${RESET}`);
118
+ for (const slot of projectMissing) {
119
+ const field = slot.replace("project.", "");
120
+ console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
121
+ }
122
+ }
123
+
124
+ if (stackMissing.length > 0) {
125
+ console.log(` ${DIM}stack:${RESET}`);
126
+ for (const slot of stackMissing) {
127
+ const field = slot.replace("stack.", "");
128
+ console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
129
+ }
130
+ }
131
+
132
+ if (humanMissing.length > 0) {
133
+ console.log(` ${DIM}human_context:${RESET}`);
134
+ for (const slot of humanMissing) {
135
+ const field = slot.replace("human_context.", "");
136
+ console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
137
+ }
138
+ }
139
+ console.log();
140
+ }
141
+ }
142
+
143
+ function getHint(field: string): string {
144
+ const hints: Record<string, string> = {
145
+ // Project
146
+ name: "Project name",
147
+ goal: "What problem does this solve?",
148
+ main_language: "TypeScript",
149
+ // Human context - questions that make you think
150
+ who: "Who is it for?",
151
+ what: "What does it do?",
152
+ why: "Why does it exist?",
153
+ where: "Where is it deployed/used?",
154
+ when: "When is it due/released?",
155
+ how: "How is it built?",
156
+ // Stack
157
+ frontend: "React",
158
+ css_framework: "Tailwind",
159
+ ui_library: "shadcn",
160
+ state_management: "zustand",
161
+ backend: "Node.js",
162
+ api_type: "REST",
163
+ runtime: "Bun",
164
+ database: "PostgreSQL",
165
+ connection: "prisma",
166
+ hosting: "Vercel",
167
+ build: "vite",
168
+ cicd: "GitHub Actions",
169
+ };
170
+ return hints[field] || "";
115
171
  }
116
172
 
117
173
  function formatBar(percent: number): string {
package/lib/scorer.ts CHANGED
@@ -110,6 +110,7 @@ export interface FafScore {
110
110
  filled: number;
111
111
  total: number;
112
112
  score: number;
113
+ missing: string[];
113
114
  }
114
115
 
115
116
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -215,11 +216,22 @@ export function calculateScore(faf: Record<string, unknown>): FafScore {
215
216
  // Calculate final score
216
217
  const score = total > 0 ? Math.round((filled / total) * 100) : 0;
217
218
 
219
+ // Find missing slots
220
+ const missing: string[] = [];
221
+ for (const category of applicableCategories) {
222
+ for (const slot of SLOTS[category]) {
223
+ if (!hasValue(faf, slot)) {
224
+ missing.push(slot);
225
+ }
226
+ }
227
+ }
228
+
218
229
  return {
219
230
  projectType,
220
231
  sections,
221
232
  filled,
222
233
  total,
223
234
  score,
235
+ missing,
224
236
  };
225
237
  }
package/lib/tier.ts CHANGED
@@ -19,6 +19,16 @@ const DIM = "\x1b[2m";
19
19
  const ORANGE = "\x1b[38;5;208m";
20
20
  const WHITE = "\x1b[37m";
21
21
 
22
+ export const TIERS: Tier[] = [
23
+ { emoji: "🏆", name: "Trophy", color: YELLOW },
24
+ { emoji: "🥇", name: "Gold", color: YELLOW },
25
+ { emoji: "🥈", name: "Silver", color: WHITE },
26
+ { emoji: "🥉", name: "Bronze", color: ORANGE },
27
+ { emoji: "🟢", name: "Green", color: GREEN },
28
+ { emoji: "🟡", name: "Yellow", color: YELLOW },
29
+ { emoji: "🔴", name: "Red", color: RED },
30
+ ];
31
+
22
32
  export function getTier(score: number): Tier {
23
33
  if (score >= 105) return { emoji: "🍊", name: "Big Orange", color: ORANGE };
24
34
  if (score >= 100) return { emoji: "🏆", name: "Trophy", color: YELLOW };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-sticky",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Fastest bun under the sum. FAF scoring CLI for Bun.",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -1,117 +1,5 @@
1
1
  // Bun Snapshot v1, https://bun.sh/docs/test/snapshots
2
2
 
3
- exports[`Tier 9: Snapshot Testing T9.01 - SLOTS structure snapshot 1`] = `
4
- {
5
- "backend": [
6
- "stack.backend",
7
- "stack.api_type",
8
- "stack.runtime",
9
- "stack.database",
10
- "stack.connection",
11
- ],
12
- "frontend": [
13
- "stack.frontend",
14
- "stack.css_framework",
15
- "stack.ui_library",
16
- "stack.state_management",
17
- ],
18
- "human": [
19
- "human_context.who",
20
- "human_context.what",
21
- "human_context.why",
22
- "human_context.where",
23
- "human_context.when",
24
- "human_context.how",
25
- ],
26
- "project": [
27
- "project.name",
28
- "project.goal",
29
- "project.main_language",
30
- ],
31
- "universal": [
32
- "stack.hosting",
33
- "stack.build",
34
- "stack.cicd",
35
- ],
36
- }
37
- `;
38
-
39
- exports[`Tier 9: Snapshot Testing T9.02 - TYPE_CATEGORIES snapshot 1`] = `
40
- {
41
- "api": [
42
- "project",
43
- "backend",
44
- "universal",
45
- "human",
46
- ],
47
- "cli": [
48
- "project",
49
- "human",
50
- ],
51
- "fullstack": [
52
- "project",
53
- "frontend",
54
- "backend",
55
- "universal",
56
- "human",
57
- ],
58
- "library": [
59
- "project",
60
- "human",
61
- ],
62
- "mobile": [
63
- "project",
64
- "human",
65
- ],
66
- "unknown": [
67
- "project",
68
- "human",
69
- ],
70
- "webapp": [
71
- "project",
72
- "frontend",
73
- "universal",
74
- "human",
75
- ],
76
- }
77
- `;
78
-
79
- exports[`Tier 9: Snapshot Testing T9.03 - Full CLI score result snapshot 1`] = `
80
- {
81
- "filled": 9,
82
- "projectType": "cli",
83
- "score": 100,
84
- "sections": {
85
- "backend": {
86
- "filled": 0,
87
- "percentage": 0,
88
- "total": 0,
89
- },
90
- "frontend": {
91
- "filled": 0,
92
- "percentage": 0,
93
- "total": 0,
94
- },
95
- "human": {
96
- "filled": 6,
97
- "percentage": 100,
98
- "total": 6,
99
- },
100
- "project": {
101
- "filled": 3,
102
- "percentage": 100,
103
- "total": 3,
104
- },
105
- "universal": {
106
- "filled": 0,
107
- "percentage": 0,
108
- "total": 0,
109
- },
110
- },
111
- "total": 9,
112
- }
113
- `;
114
-
115
3
  exports[`Tier 2: Scoring Engine T2.07 - SLOTS structure snapshot 1`] = `
116
4
  {
117
5
  "backend": [
@@ -151,6 +39,7 @@ exports[`Tier 2: Scoring Engine T2.07 - SLOTS structure snapshot 1`] = `
151
39
  exports[`Tier 2: Scoring Engine T2.14 - Full CLI result snapshot 1`] = `
152
40
  {
153
41
  "filled": 9,
42
+ "missing": [],
154
43
  "projectType": "cli",
155
44
  "score": 100,
156
45
  "sections": {