@claude-code-mastery/starter-kit 1.0.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/.claude/.starter-kit/profiles/clean.md +113 -0
- package/.claude/.starter-kit/profiles/go.md +458 -0
- package/.claude/.starter-kit/profiles/node.md +429 -0
- package/.claude/.starter-kit/profiles/python.md +475 -0
- package/.claude/.starter-kit/shared/analytics-rybbit.md +55 -0
- package/.claude/.starter-kit/shared/claude-md-base.md +93 -0
- package/.claude/.starter-kit/shared/deployment-dokploy.md +158 -0
- package/.claude/.starter-kit/shared/feature-manifest.md +43 -0
- package/.claude/.starter-kit/shared/mcp-and-pooler.md +38 -0
- package/.claude/.starter-kit/shared/mongo-setup.md +20 -0
- package/.claude/.starter-kit/shared/profile-config.md +65 -0
- package/.claude/.starter-kit/shared/seo.md +113 -0
- package/.claude/.starter-kit/shared/sql-setup.md +37 -0
- package/.claude/commands/add-feature.md +349 -0
- package/.claude/commands/add-project-setup.md +156 -0
- package/.claude/commands/architecture.md +27 -0
- package/.claude/commands/commit.md +61 -0
- package/.claude/commands/convert-project-to-starter-kit.md +508 -0
- package/.claude/commands/create-api.md +385 -0
- package/.claude/commands/create-e2e.md +230 -0
- package/.claude/commands/diagram.md +301 -0
- package/.claude/commands/help.md +120 -0
- package/.claude/commands/install-global.md +145 -0
- package/.claude/commands/new-project.md +244 -0
- package/.claude/commands/optimize-docker.md +352 -0
- package/.claude/commands/progress.md +61 -0
- package/.claude/commands/projects-created.md +79 -0
- package/.claude/commands/quickstart.md +105 -0
- package/.claude/commands/refactor.md +267 -0
- package/.claude/commands/remove-project.md +95 -0
- package/.claude/commands/review.md +59 -0
- package/.claude/commands/security-check.md +77 -0
- package/.claude/commands/set-project-profile-default.md +79 -0
- package/.claude/commands/setup.md +337 -0
- package/.claude/commands/show-user-guide.md +58 -0
- package/.claude/commands/starter-kit.md +90 -0
- package/.claude/commands/test-plan.md +118 -0
- package/.claude/commands/update-project.md +413 -0
- package/.claude/commands/what-is-my-ai-doing.md +42 -0
- package/.claude/commands/worktree.md +124 -0
- package/.claude/hooks/block-dangerous-bash.py +55 -0
- package/.claude/hooks/check-branch.sh +116 -0
- package/.claude/hooks/check-e2e.sh +71 -0
- package/.claude/hooks/check-env-sync.sh +41 -0
- package/.claude/hooks/check-file-length.py +47 -0
- package/.claude/hooks/check-ports.sh +59 -0
- package/.claude/hooks/check-rulecatch.sh +33 -0
- package/.claude/hooks/check-rybbit.sh +63 -0
- package/.claude/hooks/lint-on-save.sh +59 -0
- package/.claude/hooks/verify-no-secrets.sh +80 -0
- package/.claude/settings.json +34 -0
- package/.claude/skills/api-conventions/SKILL.md +34 -0
- package/.claude/skills/code-review/SKILL.md +87 -0
- package/.claude/skills/code-review/references/mongodb-checks.md +25 -0
- package/.claude/skills/code-review/references/project-checks.md +38 -0
- package/.claude/skills/create-service/SKILL.md +222 -0
- package/.claude/skills/debugger/SKILL.md +39 -0
- package/.claude/skills/dependency-vetting/SKILL.md +46 -0
- package/.claude/skills/design-review/SKILL.md +50 -0
- package/.claude/skills/mcp-builder/SKILL.md +57 -0
- package/.claude/skills/mongodb-rules/SKILL.md +62 -0
- package/.claude/skills/terminal-tui/SKILL.md +106 -0
- package/.claude/skills/test-writer/SKILL.md +78 -0
- package/LICENSE +21 -0
- package/README.md +2152 -0
- package/bin/cli.js +205 -0
- package/claude-mastery-project.conf +220 -0
- package/global-claude-md/CLAUDE.md +212 -0
- package/global-claude-md/settings.json +3 -0
- package/package.json +81 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tui-builder
|
|
3
|
+
description: Use when building or modifying an interactive terminal UI (TUI), especially with Ink + React on Node, or when hitting terminal-resize rendering bugs — ghost/repeated content, a blank screen after resize, or a divider/layout that won't update width. Carries battle-tested resize rules Claude otherwise gets wrong. Not for plain non-interactive CLIs (flag parsing, scripts) with no full-screen rendering.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- Building or modifying an interactive terminal UI (TUI), especially Ink + React on Node
|
|
6
|
+
- Adding or fixing terminal-resize handling in a TUI
|
|
7
|
+
- Debugging resize artifacts: ghosted/repeated content, blank screen after resize, stale divider/width
|
|
8
|
+
- Do NOT use for plain non-interactive CLIs (argument parsing, batch scripts) with no live rendering
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Building Terminal UIs (TUIs)
|
|
12
|
+
|
|
13
|
+
Battle-tested rules from shipping Ink + React TUIs. The deep stack documented here is Ink/React on Node; the underlying principles — render into the alternate screen, control resize-handler ordering, treat size as state — transfer to other TUI frameworks even when the exact escape codes differ.
|
|
14
|
+
|
|
15
|
+
The thing to get right, because it's gotten wrong every time, is terminal **resize**. Ink renders by diffing its previous output and repositioning the cursor, not by clearing and redrawing. On resize that model breaks three ways at once:
|
|
16
|
+
|
|
17
|
+
- content Ink no longer "owns" stays visible
|
|
18
|
+
- the cursor lands at an unknown position, so Ink's positioning math is off
|
|
19
|
+
- a taller terminal re-reveals old lines that were previously offscreen
|
|
20
|
+
|
|
21
|
+
Every resize bug below is a variation of this one root cause.
|
|
22
|
+
|
|
23
|
+
## Rule 1 — Render into the alternate screen buffer
|
|
24
|
+
|
|
25
|
+
**Prevents:** ghost content — old frames stacking up, the logo repeating, a scrollbar appearing on resize.
|
|
26
|
+
|
|
27
|
+
**Why:** Ink writes to the normal terminal buffer, which has scrollback. Shrink the window and the terminal shows scrollback; grow it and old output is still in the buffer at the now-visible positions. Ink overwrites from the cursor but can't erase content it no longer knows about. The alternate screen has no scrollback at all, so there's nothing to bleed in from.
|
|
28
|
+
|
|
29
|
+
Enter with `\x1B[?1049h` before `render()`, exit with `\x1B[?1049l`. Hide the cursor (`\x1B[?25l`) while you're in it — cleaner, and it avoids cursor flicker on re-renders.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Before render():
|
|
33
|
+
process.stdout.write('\x1B[?1049h\x1B[?25l'); // enter alt screen, hide cursor
|
|
34
|
+
const restoreTerminal = () => process.stdout.write('\x1B[?25h\x1B[?1049l');
|
|
35
|
+
process.once('exit', restoreTerminal);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Always register the `process.once('exit', restoreTerminal)` handler so the terminal is restored even on crash. Skip it and you strand the user in alt-screen mode after the program exits.
|
|
39
|
+
|
|
40
|
+
## Rule 2 — Register the clear handler in the entry point, BEFORE `render()`
|
|
41
|
+
|
|
42
|
+
**Prevents:** the blank screen after resize — everything disappears except the active-tab highlight until the user presses a key.
|
|
43
|
+
|
|
44
|
+
**Why:** Node fires resize listeners in registration order. If you put the clear in a `useEffect`, it registers *after* `render()` (effects run post-mount, and `render()` is where Ink registers its own handler). So on resize, Ink's handler fires first and draws the full TUI, then your clear fires second and wipes everything Ink just drew. Ink thinks the frame is already drawn, so it doesn't redraw until the next state change.
|
|
45
|
+
|
|
46
|
+
Register the clear in the entry point before `render()`, so it ends up first in the queue and Ink draws onto an already-clean screen.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// index.ts — register BEFORE render()
|
|
50
|
+
const clearOnResize = () => process.stdout.write('\x1B[2J\x1B[H');
|
|
51
|
+
process.stdout.on('resize', clearOnResize);
|
|
52
|
+
|
|
53
|
+
// Now call render() — Ink registers its handler here, AFTER ours
|
|
54
|
+
const { waitUntilExit } = render(...);
|
|
55
|
+
|
|
56
|
+
// Clean up on exit
|
|
57
|
+
process.stdout.off('resize', clearOnResize);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Rule 3 — Track terminal width as React state
|
|
61
|
+
|
|
62
|
+
**Prevents:** the stale divider — width-dependent content (dividers, layout math) keeps its old width after resize until the user presses a key.
|
|
63
|
+
|
|
64
|
+
**Why:** Values computed from `process.stdout.columns` are read once at render time. After resize the columns value updates, but nothing triggers a React re-render. Make width state so the change propagates.
|
|
65
|
+
|
|
66
|
+
The resize handler in the root component does exactly one thing: update state. No escape codes, no clearing in here (the clear lives in the entry point, Rule 2).
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// App.tsx
|
|
70
|
+
const [termWidth, setTermWidth] = useState(process.stdout.columns ?? 80);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const onResize = () => setTermWidth(process.stdout.columns ?? 80);
|
|
74
|
+
process.stdout.on('resize', onResize);
|
|
75
|
+
return () => { process.stdout.off('resize', onResize); };
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
// Use termWidth for anything that depends on terminal width
|
|
79
|
+
const divider = '─'.repeat(termWidth);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This handler registers after Ink's (correct), fires third, and triggers a second render. Ink diffs and updates only the changed line.
|
|
83
|
+
|
|
84
|
+
## Correct resize event order
|
|
85
|
+
|
|
86
|
+
Order is the whole game. When it's right:
|
|
87
|
+
|
|
88
|
+
1. `clearOnResize` (entry point, registered first) — clears the screen, cursor to (0,0).
|
|
89
|
+
2. Ink's internal handler (second) — recalculates layout with new dimensions, draws the full TUI onto the clean screen.
|
|
90
|
+
3. `setTermWidth` (root component, third) — the state update triggers a second render; Ink diffs and updates only the width-dependent lines.
|
|
91
|
+
|
|
92
|
+
## Never
|
|
93
|
+
|
|
94
|
+
- **Never use `\x1Bc` (RIS, reset to initial state).** It resets the entire terminal — colors, font, cursor settings — and behaves differently across emulators. Use `\x1B[2J\x1B[H` (clear + cursor home).
|
|
95
|
+
- **Never put the clear in a `useEffect`.** Effects run after mount, after Ink registers its handlers, so your clear will always fire after Ink draws.
|
|
96
|
+
- **Never write escape codes inside a component's render path** (outside effects). They fire during React's render phase and fight Ink's output buffer.
|
|
97
|
+
- **Never split resize logic across multiple conflicting handlers.** The clear lives in the entry point; state updates live in the root component. One job each, or the ordering becomes fragile.
|
|
98
|
+
|
|
99
|
+
## New-TUI checklist
|
|
100
|
+
|
|
101
|
+
- [ ] Alternate screen buffer (`\x1B[?1049h` / `\x1B[?1049l`) entered from the entry point
|
|
102
|
+
- [ ] Clear handler (`\x1B[2J\x1B[H`) registered in the entry point BEFORE `render()`
|
|
103
|
+
- [ ] Terminal width tracked as state in the root component
|
|
104
|
+
- [ ] Resize handler updates state only — no escape codes
|
|
105
|
+
- [ ] `process.once('exit', ...)` restores the terminal on exit and on crash
|
|
106
|
+
- [ ] Resize handlers cleaned up (`process.stdout.off(...)`) when the TUI exits
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-writer
|
|
3
|
+
description: Write tests that catch bugs, with explicit assertions, realistic data, and proper structure. Use when asked to write, add, improve, or expand tests, or to raise coverage. Writes test files and runs them to confirm they assert real behavior.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User asks to write tests, add coverage, or improve existing tests
|
|
6
|
+
- New code needs a test, or a bug needs a regression test
|
|
7
|
+
- User says "write tests", "add a test", "cover this"
|
|
8
|
+
allowed-tools: "Read, Write, Grep, Glob, Bash"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Test Writer
|
|
12
|
+
|
|
13
|
+
You write tests that catch bugs, not tests that pass. A test that can't fail isn't a test.
|
|
14
|
+
|
|
15
|
+
## Principles
|
|
16
|
+
|
|
17
|
+
1. Every test has explicit assertions. "Page loads" is not a test.
|
|
18
|
+
2. Test behavior, not implementation details.
|
|
19
|
+
3. Cover the happy path, the error cases, and the edge cases.
|
|
20
|
+
4. Use realistic test data, never `test` / `asdf`.
|
|
21
|
+
5. Tests are independent. No shared mutable state between them.
|
|
22
|
+
|
|
23
|
+
## Structure
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
describe('[Feature]', () => {
|
|
27
|
+
describe('[Scenario]', () => {
|
|
28
|
+
it('should [expected behavior] when [condition]', async () => {
|
|
29
|
+
// Arrange — set up test data
|
|
30
|
+
// Act — perform the action
|
|
31
|
+
// Assert — verify SPECIFIC outcomes
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Assertions
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// GOOD — explicit, specific
|
|
41
|
+
expect(result.status).toBe(200);
|
|
42
|
+
expect(result.body.user.email).toBe('ada@example.com');
|
|
43
|
+
await expect(page.locator('h1')).toContainText('Welcome');
|
|
44
|
+
|
|
45
|
+
// BAD — passes even when broken
|
|
46
|
+
expect(result).toBeTruthy(); // too vague
|
|
47
|
+
await page.goto('/dashboard'); // no assertion at all
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Data-layer tests (this codebase)
|
|
51
|
+
|
|
52
|
+
The data layer has rules that test data must respect, or the test passes while masking the exact bug that bites in production.
|
|
53
|
+
|
|
54
|
+
- **Seed real `ObjectId` values, not string ids.** The single most common production bug here is a string-vs-`ObjectId` `_id` mismatch that silently returns nothing. A test seeded with string ids passes and hides it. Use actual `ObjectId` types in fixtures.
|
|
55
|
+
- **Exercise the StrictDB adapter, not a hand-rolled driver mock.** Tests go through the same `adapters/` boundary the handlers use. Mock at the network or data boundary, not by reimplementing the driver.
|
|
56
|
+
- **Test the round trip.** Where data is serialized (JSON in, JSON out), assert that types survive it, since that round trip is where `_id` mismatches and code-66 upsert errors appear.
|
|
57
|
+
|
|
58
|
+
## Unit tests (Vitest)
|
|
59
|
+
|
|
60
|
+
Each test verifies:
|
|
61
|
+
|
|
62
|
+
1. Return value matches expected.
|
|
63
|
+
2. Side effects occurred, or provably didn't.
|
|
64
|
+
3. Error cases throw the proper error.
|
|
65
|
+
4. Edge cases: null, empty, max values, and for ids, wrong-type ids.
|
|
66
|
+
|
|
67
|
+
## E2E tests (Playwright)
|
|
68
|
+
|
|
69
|
+
Each test verifies:
|
|
70
|
+
|
|
71
|
+
1. Correct URL after navigation.
|
|
72
|
+
2. Key elements are present.
|
|
73
|
+
3. Correct data is displayed.
|
|
74
|
+
4. Error states show the proper message.
|
|
75
|
+
|
|
76
|
+
## Before finishing
|
|
77
|
+
|
|
78
|
+
Run the tests. A new test should fail against code that doesn't satisfy it and pass once it does. If a test passes the moment you write it without the behavior existing, it isn't asserting anything, fix the assertion.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TheDecipherist
|
|
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.
|