bun-sticky 1.0.1 → 1.0.3
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/.claude/commands/score.md +12 -0
- package/.claude/commands/test.md +15 -0
- package/.github/FUNDING.yml +1 -1
- package/.github/workflows/ci.yml +0 -3
- package/CLAUDE.md +73 -56
- package/LICENSE +21 -0
- package/PUBLISH-PROTOCOL.md +4 -2
- package/README.md +3 -3
- package/index.ts +62 -34
- package/lib/tier.ts +10 -0
- package/package.json +1 -1
- package/project.faf +1 -1
- package/tests/wjttc.test.ts +966 -0
|
@@ -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 (328+)
|
|
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
|
package/.github/FUNDING.yml
CHANGED
package/.github/workflows/ci.yml
CHANGED
package/CLAUDE.md
CHANGED
|
@@ -1,81 +1,98 @@
|
|
|
1
|
-
#
|
|
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
|
+
**🏆 100% Trophy** - 9/9 slots filled
|
|
6
|
+
|
|
7
|
+
## Quick Commands
|
|
4
8
|
|
|
5
|
-
## Quick Start
|
|
6
9
|
```bash
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
bun test # Run WJTTC test suite (328 tests)
|
|
11
|
+
bun run index.ts score # Score current project
|
|
12
|
+
bun run index.ts help # Show commands
|
|
13
|
+
bun publish # Publish to npm (see PUBLISH-PROTOCOL.md)
|
|
10
14
|
```
|
|
11
15
|
|
|
12
16
|
## Architecture
|
|
17
|
+
|
|
13
18
|
```
|
|
14
19
|
bun-sticky/
|
|
15
|
-
├── index.ts
|
|
20
|
+
├── index.ts # CLI entry + ASCII banner
|
|
16
21
|
├── lib/
|
|
17
|
-
│ ├── parser.ts
|
|
18
|
-
│ ├── scorer.ts
|
|
19
|
-
│ └── tier.ts
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
└──
|
|
22
|
+
│ ├── parser.ts # Zero-dep YAML parser
|
|
23
|
+
│ ├── scorer.ts # Wolfejam 21-slot scoring
|
|
24
|
+
│ └── tier.ts # 7-tier ranking system
|
|
25
|
+
└── tests/
|
|
26
|
+
├── sticky.test.ts # Core unit tests
|
|
27
|
+
└── wjttc.test.ts # Championship test suite
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
##
|
|
30
|
+
## Scoring System
|
|
31
|
+
|
|
32
|
+
**Wolfejam Slot-Based Scoring** (NOT Elon weights):
|
|
26
33
|
|
|
27
|
-
**Wolfejam Slot-Based Scoring** (NOT Elon weights)
|
|
28
34
|
- 21 total slots across 5 categories
|
|
29
|
-
- Type-aware: CLI=9
|
|
30
|
-
- Score = Filled
|
|
35
|
+
- Type-aware: CLI=9, Fullstack=21, etc.
|
|
36
|
+
- Formula: `Score = (Filled / Applicable) × 100`
|
|
37
|
+
|
|
38
|
+
| Category | Slots | Fields |
|
|
39
|
+
|----------|-------|--------|
|
|
40
|
+
| Project | 3 | name, goal, main_language |
|
|
41
|
+
| Frontend | 4 | frontend, css_framework, ui_library, state_management |
|
|
42
|
+
| Backend | 5 | backend, api_type, runtime, database, connection |
|
|
43
|
+
| Universal | 3 | hosting, build, cicd |
|
|
44
|
+
| Human | 6 | who, what, why, where, when, how |
|
|
45
|
+
|
|
46
|
+
## Tier System
|
|
31
47
|
|
|
32
|
-
**Tier System**
|
|
33
48
|
| Score | Tier | Emoji |
|
|
34
49
|
|-------|------|-------|
|
|
35
|
-
| 100%
|
|
36
|
-
| 99%+
|
|
37
|
-
| 95%+
|
|
38
|
-
| 85%+
|
|
39
|
-
| 70%+
|
|
40
|
-
| 55%+
|
|
41
|
-
| <55%
|
|
50
|
+
| 100% | Trophy | 🏆 |
|
|
51
|
+
| 99%+ | Gold | 🥇 |
|
|
52
|
+
| 95%+ | Silver | 🥈 |
|
|
53
|
+
| 85%+ | Bronze | 🥉 |
|
|
54
|
+
| 70%+ | Green | 🟢 |
|
|
55
|
+
| 55%+ | Yellow | 🟡 |
|
|
56
|
+
| <55% | Red | 🔴 |
|
|
42
57
|
|
|
43
58
|
## Key Files
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| `lib/scorer.ts` |
|
|
48
|
-
| `lib/scorer.ts` |
|
|
49
|
-
| `lib/
|
|
50
|
-
| `lib/
|
|
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 |
|
|
59
|
+
|
|
60
|
+
| File | Purpose |
|
|
61
|
+
|------|---------|
|
|
62
|
+
| `lib/scorer.ts:16` | SLOTS definition (21 slots) |
|
|
63
|
+
| `lib/scorer.ts:68` | TYPE_CATEGORIES mapping |
|
|
64
|
+
| `lib/scorer.ts:173` | calculateScore() function |
|
|
65
|
+
| `lib/tier.ts:22` | getTier() function |
|
|
66
|
+
| `lib/parser.ts:1` | parseYaml() zero-dep parser |
|
|
66
67
|
|
|
67
68
|
## Testing
|
|
69
|
+
|
|
70
|
+
Championship-grade WJTTC test suite with full Bun test API coverage:
|
|
71
|
+
|
|
72
|
+
- `test.each` - Parametrized tests
|
|
73
|
+
- `test.concurrent` - Parallel execution
|
|
74
|
+
- `mock`, `spyOn` - Mocking
|
|
75
|
+
- Lifecycle hooks - beforeAll, afterEach, etc.
|
|
76
|
+
- Full matcher suite
|
|
77
|
+
|
|
68
78
|
```bash
|
|
69
|
-
bun test
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# WJTTC Championship Grade
|
|
79
|
+
bun test --coverage # With coverage
|
|
80
|
+
bun test --watch # Watch mode
|
|
81
|
+
CLAUDECODE=1 bun test # AI-friendly output
|
|
73
82
|
```
|
|
74
83
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
## Development Rules
|
|
85
|
+
|
|
86
|
+
1. **Zero Dependencies** - Only Bun native APIs
|
|
87
|
+
2. **No npm repeats** - Follow PUBLISH-PROTOCOL.md exactly
|
|
88
|
+
3. **Tests first** - All changes need tests
|
|
89
|
+
4. **Wolfejam slots only** - Never use Elon weights
|
|
90
|
+
|
|
91
|
+
## Publishing
|
|
92
|
+
|
|
93
|
+
**NEVER publish without explicit GO! approval.**
|
|
94
|
+
|
|
95
|
+
See `PUBLISH-PROTOCOL.md` for the complete ceremony.
|
|
79
96
|
|
|
80
97
|
---
|
|
81
|
-
*
|
|
98
|
+
*Part of FAF ecosystem. Built for Claude Code.*
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 wolfejam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PUBLISH-PROTOCOL.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Pre-Publish Checklist
|
|
4
4
|
|
|
5
5
|
### 1. Code Quality
|
|
6
|
-
- [x] All
|
|
6
|
+
- [x] All 328 tests passing (`bun test`)
|
|
7
7
|
- [x] Zero TypeScript errors
|
|
8
8
|
- [x] Zero runtime dependencies
|
|
9
9
|
- [x] Pure Bun APIs only
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
### 3. Test Suite
|
|
19
19
|
```bash
|
|
20
20
|
bun test
|
|
21
|
-
# Expect:
|
|
21
|
+
# Expect: 328 pass, 0 fail
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
### 4. Manual Verification
|
|
@@ -54,6 +54,8 @@ bunx bun-sticky help
|
|
|
54
54
|
|---------|------|-------|
|
|
55
55
|
| 1.0.0 | 2024-12-22 | Initial release - Wolfejam slot-based scoring, 177 tests |
|
|
56
56
|
| 1.0.1 | 2024-12-22 | Proper publish ceremony, version consistency |
|
|
57
|
+
| 1.0.2 | 2025-12-22 | Test fixes (rounding, TIERS export, Empty tier) |
|
|
58
|
+
| 1.0.3 | 2025-12-22 | Fix sync command overwriting CLAUDE.md |
|
|
57
59
|
|
|
58
60
|
---
|
|
59
61
|
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ bunx bun-sticky score
|
|
|
18
18
|
████ █▄▀ ▀▄▀ █ █
|
|
19
19
|
▀▀
|
|
20
20
|
|
|
21
|
-
🥐 Bun Sticky v1.0.
|
|
21
|
+
🥐 Bun Sticky v1.0.3 .faf CLI
|
|
22
22
|
Fastest bun under the sum.
|
|
23
23
|
|
|
24
24
|
────────────────────────────────────────────────
|
|
@@ -29,7 +29,7 @@ bunx bun-sticky score
|
|
|
29
29
|
Project ████████████ 3/3
|
|
30
30
|
Human ████████░░░░ 4/6
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
🟢 78% Green
|
|
33
33
|
Filled: 7/9 slots
|
|
34
34
|
```
|
|
35
35
|
|
|
@@ -97,7 +97,7 @@ Built for Bun's speed:
|
|
|
97
97
|
|
|
98
98
|
## Testing
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
328 tests. Championship-grade WJTTC test suite.
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
bun test
|
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.
|
|
24
|
-
|
|
25
|
-
//
|
|
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.3";
|
|
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
|
-
|
|
38
|
+
────────────────────────────────────────────────
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
▄▄ ▄▀▀▀ ▀█▀ █ ▄▀▀ █▄▀ █ █
|
|
41
|
+
████ ▀▀█▄ █ █ █ █▀▄ █
|
|
42
|
+
██████ ▄▄▄▀ █ █ ▀▀▀ █ █ █
|
|
43
|
+
████████
|
|
44
|
+
████████ █▀▄ █ █ █▀▄
|
|
45
|
+
██████ ██▀ █ █ █ █
|
|
46
|
+
████ █▄▀ ▀▄▀ █ █
|
|
47
|
+
▀▀
|
|
58
48
|
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
🥐 Bun Sticky v${VERSION} .faf CLI
|
|
50
|
+
Fastest bun under the sum.
|
|
61
51
|
|
|
62
|
-
|
|
52
|
+
────────────────────────────────────────────────
|
|
63
53
|
`;
|
|
64
54
|
|
|
65
55
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -127,7 +117,7 @@ async function cmdScore(): Promise<void> {
|
|
|
127
117
|
console.log(` ${DIM}project:${RESET}`);
|
|
128
118
|
for (const slot of projectMissing) {
|
|
129
119
|
const field = slot.replace("project.", "");
|
|
130
|
-
console.log(` ${
|
|
120
|
+
console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
|
|
131
121
|
}
|
|
132
122
|
}
|
|
133
123
|
|
|
@@ -135,7 +125,7 @@ async function cmdScore(): Promise<void> {
|
|
|
135
125
|
console.log(` ${DIM}stack:${RESET}`);
|
|
136
126
|
for (const slot of stackMissing) {
|
|
137
127
|
const field = slot.replace("stack.", "");
|
|
138
|
-
console.log(` ${
|
|
128
|
+
console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
|
|
139
129
|
}
|
|
140
130
|
}
|
|
141
131
|
|
|
@@ -143,7 +133,7 @@ async function cmdScore(): Promise<void> {
|
|
|
143
133
|
console.log(` ${DIM}human_context:${RESET}`);
|
|
144
134
|
for (const slot of humanMissing) {
|
|
145
135
|
const field = slot.replace("human_context.", "");
|
|
146
|
-
console.log(` ${
|
|
136
|
+
console.log(` ${DIM}${field}:${RESET} "${getHint(field)}"`);
|
|
147
137
|
}
|
|
148
138
|
}
|
|
149
139
|
console.log();
|
|
@@ -247,20 +237,58 @@ async function cmdSync(): Promise<void> {
|
|
|
247
237
|
const result = calculateScore(faf);
|
|
248
238
|
const tier = getTier(result.score);
|
|
249
239
|
|
|
250
|
-
|
|
251
|
-
const
|
|
240
|
+
const scoreBadge = `**${tier.emoji} ${result.score}% ${tier.name}** - ${result.filled}/${result.total} slots filled`;
|
|
241
|
+
const claudeFile = Bun.file("CLAUDE.md");
|
|
242
|
+
|
|
243
|
+
if (await claudeFile.exists()) {
|
|
244
|
+
// Update existing CLAUDE.md - preserve content, update/insert score badge
|
|
245
|
+
let existing = await claudeFile.text();
|
|
246
|
+
const badgePattern = /^\*\*[🏆🥇🥈🥉🟢🟡🔴⚪🍊]\s*\d+%.*\*\*.*slots filled$/mu;
|
|
247
|
+
|
|
248
|
+
if (badgePattern.test(existing)) {
|
|
249
|
+
// Replace existing badge
|
|
250
|
+
existing = existing.replace(badgePattern, scoreBadge);
|
|
251
|
+
} else {
|
|
252
|
+
// Insert badge after first paragraph (after title + description)
|
|
253
|
+
const lines = existing.split("\n");
|
|
254
|
+
let insertIndex = 1;
|
|
255
|
+
|
|
256
|
+
// Find first empty line after title
|
|
257
|
+
for (let i = 1; i < lines.length; i++) {
|
|
258
|
+
if (lines[i].trim() === "") {
|
|
259
|
+
insertIndex = i + 1;
|
|
260
|
+
// Skip consecutive empty lines
|
|
261
|
+
while (insertIndex < lines.length && lines[insertIndex].trim() === "") {
|
|
262
|
+
insertIndex++;
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
252
267
|
|
|
253
|
-
|
|
268
|
+
// Check if next line is already a heading, insert before it
|
|
269
|
+
if (lines[insertIndex]?.startsWith("#")) {
|
|
270
|
+
lines.splice(insertIndex, 0, scoreBadge, "");
|
|
271
|
+
} else {
|
|
272
|
+
lines.splice(insertIndex, 0, "", scoreBadge);
|
|
273
|
+
}
|
|
274
|
+
existing = lines.join("\n");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await Bun.write("CLAUDE.md", existing);
|
|
278
|
+
} else {
|
|
279
|
+
// Create new minimal CLAUDE.md
|
|
280
|
+
const claudeMd = `# ${name}
|
|
254
281
|
|
|
255
|
-
|
|
282
|
+
${goal}
|
|
256
283
|
|
|
257
|
-
|
|
284
|
+
${scoreBadge}
|
|
258
285
|
|
|
259
286
|
---
|
|
260
287
|
*Synced by Bun Sticky*
|
|
261
288
|
`;
|
|
289
|
+
await Bun.write("CLAUDE.md", claudeMd);
|
|
290
|
+
}
|
|
262
291
|
|
|
263
|
-
await Bun.write("CLAUDE.md", claudeMd);
|
|
264
292
|
console.log(BANNER);
|
|
265
293
|
console.log(` ${GREEN}Synced${RESET} project.faf → CLAUDE.md`);
|
|
266
294
|
console.log();
|
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