codebyplan 1.13.49 → 1.13.51
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/dist/cli.js +447 -14
- package/package.json +1 -1
- package/templates/agents/cbp-round-executor.md +1 -6
- package/templates/agents/cbp-task-planner.md +2 -2
- package/templates/hooks/cbp-skill-context-guard.sh +52 -0
- package/templates/hooks/cbp-test-hooks.sh +144 -0
- package/templates/hooks/hooks.json +9 -0
- package/templates/rules/model-invocation-convention.md +40 -0
- package/templates/rules/parallel-waves.md +1 -1
- package/templates/rules/supabase-branch-lifecycle.md +2 -3
- package/templates/rules/task-routing-recommendation.md +1 -1
- package/templates/settings.project.base.json +2 -3
- package/templates/skills/cbp-build-cc-mode/SKILL.md +1 -1
- package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +42 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +1 -0
- package/templates/skills/cbp-checkpoint-start/SKILL.md +1 -0
- package/templates/skills/cbp-clear-continue/SKILL.md +86 -0
- package/templates/skills/cbp-clear-prep/SKILL.md +121 -0
- package/templates/skills/cbp-round-start/SKILL.md +1 -1
- package/templates/skills/cbp-session-end/SKILL.md +2 -18
- package/templates/skills/cbp-session-start/SKILL.md +4 -18
- package/templates/skills/cbp-supabase-migrate/SKILL.md +1 -1
- package/templates/skills/cbp-task-check/SKILL.md +12 -5
- package/templates/skills/cbp-task-complete/SKILL.md +9 -11
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +14 -21
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +4 -6
- package/templates/skills/cbp-task-testing/SKILL.md +9 -14
- package/templates/skills/cbp-frontend-a11y/SKILL.md +0 -108
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +0 -130
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +0 -122
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +0 -154
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +0 -111
- package/templates/skills/cbp-git-worktree-create/SKILL.md +0 -199
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +0 -144
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# Contrast and Visual Accessibility Reference
|
|
2
|
-
|
|
3
|
-
## WCAG 2.1 AA Contrast Requirements
|
|
4
|
-
|
|
5
|
-
All text and UI components must meet these minimum contrast ratios:
|
|
6
|
-
|
|
7
|
-
| Content type | Minimum ratio | Standard |
|
|
8
|
-
|-------------|--------------|---------|
|
|
9
|
-
| Normal text (< 18pt / < 14pt bold) | **4.5:1** | WCAG 2.1 AA SC 1.4.3 |
|
|
10
|
-
| Large text (≥ 18pt / ≥ 14pt bold) | **3:1** | WCAG 2.1 AA SC 1.4.3 |
|
|
11
|
-
| UI components (borders, icons, form controls) | **3:1** | WCAG 2.1 AA SC 1.4.11 |
|
|
12
|
-
| Graphical objects (data chart elements, icons conveying meaning) | **3:1** | WCAG 2.1 AA SC 1.4.11 |
|
|
13
|
-
|
|
14
|
-
**Decorative** elements (purely ornamental, no meaning) are EXEMPT.
|
|
15
|
-
|
|
16
|
-
### Verification
|
|
17
|
-
|
|
18
|
-
Use design tokens from `packages/design-tokens/` to derive hex values, then verify with a contrast checker:
|
|
19
|
-
|
|
20
|
-
- Browser DevTools Accessibility panel
|
|
21
|
-
- `npx @accessibility-checker/cli` against the rendered component
|
|
22
|
-
- Design-tool plugins (Figma Contrast, Stark)
|
|
23
|
-
|
|
24
|
-
If a token pair is new, record the ratio in a comment in the SCSS: `/* contrast: 5.2:1 vs --color-surface */`.
|
|
25
|
-
|
|
26
|
-
## Focus Visible
|
|
27
|
-
|
|
28
|
-
Focus indicators must NEVER be suppressed via `outline: none` or `outline: 0` without providing a replacement that meets **3:1 contrast** against adjacent colour:
|
|
29
|
-
|
|
30
|
-
```scss
|
|
31
|
-
// Anti-pattern — removes all visible focus indicator
|
|
32
|
-
&:focus {
|
|
33
|
-
outline: none; // WCAG failure
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Correct — custom focus ring that meets 3:1 contrast
|
|
37
|
-
&:focus-visible {
|
|
38
|
-
outline: 2px solid var(--color-focus-ring); // 3:1 minimum
|
|
39
|
-
outline-offset: 2px;
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Use `:focus-visible` (not `:focus`) for the custom ring — `:focus-visible` suppresses the ring for mouse clicks while preserving it for keyboard navigation.
|
|
44
|
-
|
|
45
|
-
The WCAG 2.2 enhanced criterion (SC 2.4.11, AAA) requires 3:1 contrast + 2px outline area. Target this for new components.
|
|
46
|
-
|
|
47
|
-
## Colour-Only State Communication
|
|
48
|
-
|
|
49
|
-
State conveyed through colour alone is a WCAG 2.1 AA failure (SC 1.4.1). Always pair colour with at least one of: icon, text label, pattern, or shape.
|
|
50
|
-
|
|
51
|
-
| Anti-pattern | Fix |
|
|
52
|
-
|-------------|-----|
|
|
53
|
-
| Red border = error (colour only) | Red border + error icon + "Invalid email" text |
|
|
54
|
-
| Green = online (colour only) | Green dot + "Online" text label |
|
|
55
|
-
| Yellow = warning (colour only) | Yellow background + warning icon + descriptive text |
|
|
56
|
-
| Active nav item is blue (colour only) | Blue + `aria-current="page"` + underline or bold weight |
|
|
57
|
-
|
|
58
|
-
## prefers-reduced-motion
|
|
59
|
-
|
|
60
|
-
Users with vestibular disorders may configure `prefers-reduced-motion: reduce` in their OS. Honour it:
|
|
61
|
-
|
|
62
|
-
```scss
|
|
63
|
-
@keyframes slideIn {
|
|
64
|
-
from { transform: translateX(-100%); }
|
|
65
|
-
to { transform: translateX(0); }
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.panel {
|
|
69
|
-
animation: slideIn 300ms ease-out;
|
|
70
|
-
|
|
71
|
-
@media (prefers-reduced-motion: reduce) {
|
|
72
|
-
animation: none;
|
|
73
|
-
// Provide instant appearance or a fade (no translate/scale/spin)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
For JavaScript-driven animations:
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
82
|
-
if (!prefersReduced) {
|
|
83
|
-
// run animation
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Transitions that solely affect `opacity` (fade) are generally safe — opacity changes do not trigger vestibular responses. Transforms (`translate`, `scale`, `rotate`) and large motion sweeps are the primary triggers.
|
|
88
|
-
|
|
89
|
-
## Touch Target Size
|
|
90
|
-
|
|
91
|
-
Interactive elements must have a minimum tap target of **44 × 44 CSS px** (Apple HIG / WCAG 2.5.5 AAA, de-facto standard):
|
|
92
|
-
|
|
93
|
-
```scss
|
|
94
|
-
.icon-button {
|
|
95
|
-
min-width: 44px;
|
|
96
|
-
min-height: 44px;
|
|
97
|
-
display: flex;
|
|
98
|
-
align-items: center;
|
|
99
|
-
justify-content: center;
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
When the visible icon is smaller (e.g. 24px), expand the tap target with padding:
|
|
104
|
-
|
|
105
|
-
```scss
|
|
106
|
-
.icon-button {
|
|
107
|
-
padding: 10px; // 24px icon + 2×10px padding = 44px touch target
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
On mobile breakpoints specifically, verify that adjacent interactive elements have at least 8px spacing between tap target edges to prevent accidental activation.
|
|
112
|
-
|
|
113
|
-
## Text Resizing
|
|
114
|
-
|
|
115
|
-
Users who increase browser text size up to 200% must be able to read and use all content without horizontal scroll (WCAG 1.4.4). Use relative units:
|
|
116
|
-
|
|
117
|
-
- `font-size`: `rem` (relative to browser root, respects user preference)
|
|
118
|
-
- `line-height`: unitless (e.g. `1.5`) or `em`
|
|
119
|
-
- Container widths: `max-width` in `ch` or `%`, never fixed `px` for reading columns
|
|
120
|
-
- Spacing: `em` for component-internal spacing; `rem` for layout spacing
|
|
121
|
-
|
|
122
|
-
Avoid `px` for font sizes on text elements that users may need to scale.
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Keyboard Interaction Patterns Reference
|
|
2
|
-
|
|
3
|
-
Every interactive component must be fully operable with a keyboard alone. Mouse-only patterns are WCAG 2.1 AA failures (Success Criterion 2.1.1).
|
|
4
|
-
|
|
5
|
-
## Focus Management
|
|
6
|
-
|
|
7
|
-
### On Mount (Dialog / Modal / Drawer)
|
|
8
|
-
|
|
9
|
-
When a dialog, modal, or drawer opens:
|
|
10
|
-
|
|
11
|
-
1. Move focus to the FIRST interactive element inside (or to the dialog container if no interactive element exists — ensure `tabindex="0"` on the container in that case)
|
|
12
|
-
2. For modals with a clear primary action: move focus to the primary action button
|
|
13
|
-
3. For forms: move focus to the first form field
|
|
14
|
-
|
|
15
|
-
```jsx
|
|
16
|
-
// Using useEffect + ref
|
|
17
|
-
const modalRef = useRef<HTMLDivElement>(null);
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (isOpen) {
|
|
21
|
-
// Focus the first focusable element inside
|
|
22
|
-
const firstFocusable = modalRef.current?.querySelector<HTMLElement>(
|
|
23
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
24
|
-
);
|
|
25
|
-
firstFocusable?.focus();
|
|
26
|
-
}
|
|
27
|
-
}, [isOpen]);
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### On Close (Dialog / Modal / Drawer)
|
|
31
|
-
|
|
32
|
-
When a dialog, modal, or drawer closes:
|
|
33
|
-
|
|
34
|
-
1. Return focus to the element that TRIGGERED the opening (save a ref to it before opening)
|
|
35
|
-
2. If the trigger no longer exists (deleted row), focus the nearest logical element
|
|
36
|
-
|
|
37
|
-
```jsx
|
|
38
|
-
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
39
|
-
|
|
40
|
-
const handleClose = () => {
|
|
41
|
-
setIsOpen(false);
|
|
42
|
-
// Return focus to trigger
|
|
43
|
-
triggerRef.current?.focus();
|
|
44
|
-
};
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Tab Order
|
|
48
|
-
|
|
49
|
-
- Tab order must follow the logical reading/interaction order — typically top-left to bottom-right
|
|
50
|
-
- Never use `tabindex > 0` — it creates a separate tab order before natural DOM order, confusing all keyboard users
|
|
51
|
-
- `tabindex="0"` adds a non-interactive element to natural tab order (use sparingly — prefer interactive elements)
|
|
52
|
-
- `tabindex="-1"` removes from natural tab order but allows programmatic `.focus()` (correct for modal containers)
|
|
53
|
-
|
|
54
|
-
## Esc Key
|
|
55
|
-
|
|
56
|
-
Any overlay, popover, dropdown, or modal MUST close on `Escape`:
|
|
57
|
-
|
|
58
|
-
```jsx
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
61
|
-
if (e.key === 'Escape') {
|
|
62
|
-
onClose();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
if (isOpen) {
|
|
66
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
67
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
68
|
-
}
|
|
69
|
-
}, [isOpen, onClose]);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Arrow Key Navigation
|
|
73
|
-
|
|
74
|
-
For composite widgets (menu, listbox, radio group, tabs, grid), arrow keys navigate BETWEEN items — Tab moves focus OUT of the widget entirely.
|
|
75
|
-
|
|
76
|
-
| Widget | Keys |
|
|
77
|
-
|--------|------|
|
|
78
|
-
| Menu / dropdown | `↑`/`↓` between items, `Home`/`End` to first/last |
|
|
79
|
-
| Tabs | `←`/`→` between tabs (horizontal) or `↑`/`↓` (vertical) |
|
|
80
|
-
| Listbox | `↑`/`↓` between options, `Home`/`End` |
|
|
81
|
-
| Grid | `↑`/`↓`/`←`/`→` between cells |
|
|
82
|
-
| Radio group | `↑`/`↓` or `←`/`→` between radios; selection follows focus |
|
|
83
|
-
|
|
84
|
-
## Focus Traps (Modal)
|
|
85
|
-
|
|
86
|
-
While a modal is open, Tab and Shift+Tab must cycle WITHIN the modal — not escape to the page behind:
|
|
87
|
-
|
|
88
|
-
```jsx
|
|
89
|
-
const FOCUSABLE_SELECTORS =
|
|
90
|
-
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
91
|
-
|
|
92
|
-
const handleTabKey = (e: KeyboardEvent) => {
|
|
93
|
-
const focusableEls = Array.from(
|
|
94
|
-
modalRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS) ?? []
|
|
95
|
-
);
|
|
96
|
-
const first = focusableEls[0];
|
|
97
|
-
const last = focusableEls[focusableEls.length - 1];
|
|
98
|
-
|
|
99
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
100
|
-
e.preventDefault();
|
|
101
|
-
last.focus();
|
|
102
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
103
|
-
e.preventDefault();
|
|
104
|
-
first.focus();
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Library alternative: `focus-trap-react` handles this pattern with accessibility compliance. If it is already in `package.json`, use it.
|
|
110
|
-
|
|
111
|
-
## Type-Ahead (Listbox / Menu)
|
|
112
|
-
|
|
113
|
-
When a user types a character while focus is inside a listbox or menu, focus jumps to the first item whose label starts with that character. Implement only when `role="listbox"` or `role="menu"` is used with custom keyboard handling — native `<select>` has this built in.
|
|
114
|
-
|
|
115
|
-
## Enter and Space Activation
|
|
116
|
-
|
|
117
|
-
| Element | Enter | Space |
|
|
118
|
-
|---------|-------|-------|
|
|
119
|
-
| `<button>` | Activates | Activates |
|
|
120
|
-
| `<a href>` | Follows link | Scrolls page (native browser) |
|
|
121
|
-
| `<input type="checkbox">` | n/a | Toggles |
|
|
122
|
-
| `role="button"` | Must activate | Must activate |
|
|
123
|
-
| `role="menuitem"` | Activates | Activates |
|
|
124
|
-
|
|
125
|
-
For custom `role="button"` on a non-button element:
|
|
126
|
-
```jsx
|
|
127
|
-
<div
|
|
128
|
-
role="button"
|
|
129
|
-
tabIndex={0}
|
|
130
|
-
onKeyDown={(e) => {
|
|
131
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
132
|
-
e.preventDefault();
|
|
133
|
-
handleActivate();
|
|
134
|
-
}
|
|
135
|
-
}}
|
|
136
|
-
onClick={handleActivate}
|
|
137
|
-
>
|
|
138
|
-
Custom button
|
|
139
|
-
</div>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Prefer `<button>` — it handles Enter/Space natively and avoids this boilerplate.
|
|
143
|
-
|
|
144
|
-
## Skip Links
|
|
145
|
-
|
|
146
|
-
For pages with repeated navigation (header nav present on every page), provide a skip-to-main-content link as the first focusable element:
|
|
147
|
-
|
|
148
|
-
```jsx
|
|
149
|
-
<a href="#main-content" className={styles.skipLink}>
|
|
150
|
-
Skip to main content
|
|
151
|
-
</a>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Visible on focus (via CSS), hidden otherwise. The `<main id="main-content">` is the target.
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Semantic HTML Reference
|
|
2
|
-
|
|
3
|
-
Use native HTML semantics first. ARIA is a gap-filler for when no native element meets the need — not a replacement.
|
|
4
|
-
|
|
5
|
-
## Landmark Roles
|
|
6
|
-
|
|
7
|
-
| Element | Role | Notes |
|
|
8
|
-
|---------|------|-------|
|
|
9
|
-
| `<main>` | `main` | Exactly ONE per page |
|
|
10
|
-
| `<nav>` | `navigation` | Multiple allowed; each needs an `aria-label` |
|
|
11
|
-
| `<header>` | `banner` (at page scope) | In a sectioning element it becomes generic |
|
|
12
|
-
| `<footer>` | `contentinfo` (at page scope) | |
|
|
13
|
-
| `<aside>` | `complementary` | |
|
|
14
|
-
| `<section>` | `region` (only when it has an `aria-labelledby`) | Without label, it's generic |
|
|
15
|
-
|
|
16
|
-
Anti-pattern: `<div role="main">` — use `<main>` instead.
|
|
17
|
-
|
|
18
|
-
## Heading Hierarchy
|
|
19
|
-
|
|
20
|
-
- One `<h1>` per page — the page title or primary content heading
|
|
21
|
-
- Never skip levels: `h1 → h2 → h3`, not `h1 → h3`
|
|
22
|
-
- Headings convey structure, not visual size — use CSS for size; use the correct level for hierarchy
|
|
23
|
-
- Empty headings (`<h2></h2>`) violate WCAG 2.4.6
|
|
24
|
-
|
|
25
|
-
## `<button>` vs `<a>`
|
|
26
|
-
|
|
27
|
-
| Use | Element |
|
|
28
|
-
|-----|---------|
|
|
29
|
-
| Triggers an action (submit, open modal, toggle) | `<button>` |
|
|
30
|
-
| Navigates to a URL | `<a href="...">` |
|
|
31
|
-
| Downloads a file | `<a href="..." download>` |
|
|
32
|
-
|
|
33
|
-
Anti-patterns:
|
|
34
|
-
- `<div onClick={doAction}>` — not keyboard accessible; use `<button>`
|
|
35
|
-
- `<button onClick={() => router.push('/path')}>` — use `<a href="/path">`; or `<Link href="/path">` in Next.js
|
|
36
|
-
- `<a href="#">` with an onClick — use `<button>` if no real URL
|
|
37
|
-
|
|
38
|
-
## Form Elements
|
|
39
|
-
|
|
40
|
-
```html
|
|
41
|
-
<!-- Correct: explicit association -->
|
|
42
|
-
<label htmlFor="email-input">Email</label>
|
|
43
|
-
<input id="email-input" type="email" name="email" />
|
|
44
|
-
|
|
45
|
-
<!-- Correct: implicit wrapping -->
|
|
46
|
-
<label>
|
|
47
|
-
Email
|
|
48
|
-
<input type="email" name="email" />
|
|
49
|
-
</label>
|
|
50
|
-
|
|
51
|
-
<!-- Anti-pattern: no association -->
|
|
52
|
-
<span>Email</span>
|
|
53
|
-
<input type="email" name="email" />
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
For fieldsets with radio/checkbox groups:
|
|
57
|
-
```html
|
|
58
|
-
<fieldset>
|
|
59
|
-
<legend>Preferred contact method</legend>
|
|
60
|
-
<label><input type="radio" name="contact" value="email" /> Email</label>
|
|
61
|
-
<label><input type="radio" name="contact" value="phone" /> Phone</label>
|
|
62
|
-
</fieldset>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Lists
|
|
66
|
-
|
|
67
|
-
Use `<ul>/<ol>/<li>` for lists of items — screen readers announce item count and position.
|
|
68
|
-
|
|
69
|
-
Anti-pattern: `<div class="list"><div class="item">...</div></div>` — no count/position announced.
|
|
70
|
-
|
|
71
|
-
Exception: `list-style: none` on `<ul>` removes list semantics in Safari/VoiceOver. Add `role="list"` when list-style is suppressed:
|
|
72
|
-
```jsx
|
|
73
|
-
<ul role="list" style={{ listStyle: 'none' }}>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Tables
|
|
77
|
-
|
|
78
|
-
For data tables (not layout):
|
|
79
|
-
```html
|
|
80
|
-
<table>
|
|
81
|
-
<caption>Monthly sales by region</caption>
|
|
82
|
-
<thead>
|
|
83
|
-
<tr>
|
|
84
|
-
<th scope="col">Region</th>
|
|
85
|
-
<th scope="col">Sales</th>
|
|
86
|
-
</tr>
|
|
87
|
-
</thead>
|
|
88
|
-
<tbody>
|
|
89
|
-
<tr>
|
|
90
|
-
<th scope="row">North</th>
|
|
91
|
-
<td>$12,000</td>
|
|
92
|
-
</tr>
|
|
93
|
-
</tbody>
|
|
94
|
-
</table>
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
`<caption>` is the table's accessible name. `scope="col"/"row"` associates headers with cells.
|
|
98
|
-
|
|
99
|
-
Never use `<table>` for visual layout — use CSS Grid or Flexbox.
|
|
100
|
-
|
|
101
|
-
## Anti-Pattern Quick-Reference
|
|
102
|
-
|
|
103
|
-
| Anti-pattern | Fix |
|
|
104
|
-
|-------------|-----|
|
|
105
|
-
| `<div onClick>` | `<button>` or `<a>` |
|
|
106
|
-
| `<span onClick>` | `<button>` |
|
|
107
|
-
| `<img>` missing `alt` | Add `alt="description"` or `alt=""` for decorative |
|
|
108
|
-
| `<input>` missing label | Add `<label htmlFor>` or `aria-label` |
|
|
109
|
-
| `<h1>` used for styling | Use CSS; pick correct heading level |
|
|
110
|
-
| `<table>` for layout | Use CSS Grid/Flexbox |
|
|
111
|
-
| Empty `<button>` (icon only) | Add `aria-label="Close"` |
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: cbp-git-worktree-create
|
|
3
|
-
description: Create git worktree with clean command setup and register in CodeByPlan
|
|
4
|
-
argument-hint: <branch-name> e.g. codebyplan-app
|
|
5
|
-
effort: low
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Git Worktree Create
|
|
9
|
-
|
|
10
|
-
Create a git worktree as a sibling folder and register it in CodeByPlan. The worktree gets its own full copy of `.claude/` files on disk (rules, skills, hooks, agents, context) — git deduplicates at the object level so there's no real cost.
|
|
11
|
-
|
|
12
|
-
## Arguments
|
|
13
|
-
|
|
14
|
-
`$ARGUMENTS`: branch name — becomes both the branch name and the folder name.
|
|
15
|
-
|
|
16
|
-
Examples:
|
|
17
|
-
- `codebyplan-app` → folder `../codebyplan-app/`, branch `codebyplan-app`
|
|
18
|
-
- `codebyplan-package` → folder `../codebyplan-package/`, branch `codebyplan-package`
|
|
19
|
-
|
|
20
|
-
## Prerequisites
|
|
21
|
-
|
|
22
|
-
- Must run from the **main repo** (the non-worktree checkout)
|
|
23
|
-
- Working tree should be clean
|
|
24
|
-
- Branch should not already exist as a worktree
|
|
25
|
-
- New branches are always rooted at `origin/{production}` (read from `.codebyplan/git.json`), independent of the main repo's currently checked-out branch
|
|
26
|
-
|
|
27
|
-
## Instructions
|
|
28
|
-
|
|
29
|
-
### Step 1: Verify Main Repo
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
MAIN_REPO="$(git rev-parse --show-toplevel)"
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Verify this is the main repo (not itself a worktree):
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
git rev-parse --git-dir
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
If the result is a file (not `.git` directory), this is already a worktree. Stop:
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
## Error
|
|
45
|
-
|
|
46
|
-
Run this command from the main repo, not from a worktree.
|
|
47
|
-
Main repo: [path from git-common-dir parent]
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Step 2: Validate Arguments
|
|
51
|
-
|
|
52
|
-
If `$ARGUMENTS` is empty, ask the user:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
What should the worktree be called? (e.g. codebyplan-app)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Set:
|
|
59
|
-
- `BRANCH_NAME` = `$ARGUMENTS`
|
|
60
|
-
- `WORKTREE_PATH` = `$MAIN_REPO/../$BRANCH_NAME`
|
|
61
|
-
|
|
62
|
-
### Step 3: Check for Conflicts
|
|
63
|
-
|
|
64
|
-
Check if the worktree already exists:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
git worktree list | grep "$BRANCH_NAME"
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
If it exists:
|
|
71
|
-
```
|
|
72
|
-
## Worktree Already Exists
|
|
73
|
-
|
|
74
|
-
Branch `[name]` is already checked out at [path].
|
|
75
|
-
|
|
76
|
-
Use `/cbp-git-worktree-remove [name]` to remove it first.
|
|
77
|
-
```
|
|
78
|
-
Stop here.
|
|
79
|
-
|
|
80
|
-
Check if the folder already exists:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
ls -d "$WORKTREE_PATH" 2>/dev/null
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
If it exists, stop and inform the user.
|
|
87
|
-
|
|
88
|
-
### Step 4: Create Branch If Needed
|
|
89
|
-
|
|
90
|
-
Resolve the production branch: read `.codebyplan/git.json` and take `branch_config.production` (fall back to `main` if the file or field is absent). Call this `$PRODUCTION`.
|
|
91
|
-
|
|
92
|
-
Check if branch exists:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
git branch --list "$BRANCH_NAME"
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
If branch does NOT exist, create it from the freshest production tip — **not** from the main repo's ambient HEAD (which is often a stale home branch such as `codebyplan-main`):
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
git fetch origin "$PRODUCTION" 2>/dev/null || true
|
|
102
|
-
git branch "$BRANCH_NAME" "origin/$PRODUCTION"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
This guarantees every worktree starts at `origin/{production}` regardless of which branch the main repo currently has checked out.
|
|
106
|
-
|
|
107
|
-
### Step 5: Create Worktree
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Step 6: Set Up MCP Connection
|
|
114
|
-
|
|
115
|
-
Copy `.mcp.json` from the main repo to the worktree. The file is committed and contains only the public MCP URL (no secret), so this is a plain `cp` — no path rewriting or key substitution needed:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
cp "$MAIN_REPO/.mcp.json" "$WORKTREE_PATH/.mcp.json"
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Expected shape (do NOT rewrite paths — this is remote HTTP):
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"mcpServers": {
|
|
126
|
-
"codebyplan": {
|
|
127
|
-
"type": "http",
|
|
128
|
-
"url": "https://mcp.codebyplan.com/mcp"
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Step 7: Set Up Environment
|
|
135
|
-
|
|
136
|
-
Copy `.env.local` from the main repo to the worktree (contains the app's environment variables such as Supabase keys and feature flags — MCP auth is OAuth Bearer handled by Claude Code, not a key in this file):
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
cp "$MAIN_REPO/.env.local" "$WORKTREE_PATH/.env.local"
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Verify `.env.local` is already in `.gitignore` (it should be via `.env.local` pattern). If not, add it.
|
|
143
|
-
|
|
144
|
-
Also copy the gitignored E2E credentials source (`.codebyplan/e2e.env`, referenced by `.codebyplan/e2e.json`) so the new worktree can run Playwright auth flows immediately:
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
mkdir -p "$WORKTREE_PATH/.codebyplan"
|
|
148
|
-
if [ -f "$MAIN_REPO/.codebyplan/e2e.env" ]; then
|
|
149
|
-
cp "$MAIN_REPO/.codebyplan/e2e.env" "$WORKTREE_PATH/.codebyplan/e2e.env"
|
|
150
|
-
fi
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
If the main repo has no `.codebyplan/e2e.env` yet, provision it after setup by running `codebyplan ports --path "$WORKTREE_PATH" --provision-e2e` (copies the canonical E2E vars from `apps/web/.env.local`). Pass `--path` BEFORE the boolean flag. `.codebyplan/e2e.env` is gitignored — never commit it.
|
|
154
|
-
|
|
155
|
-
### Step 8: Push Branch
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
cd "$WORKTREE_PATH" && git push -u origin "$BRANCH_NAME"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Step 9: Register Worktree and Write `.codebyplan/` Config
|
|
162
|
-
|
|
163
|
-
Run `codebyplan worktree create "$BRANCH_NAME" --path "$WORKTREE_PATH"` and parse the JSON output (`{ worktree_files_written: boolean, mcp_registered: boolean, worktree_id?, warn? }`):
|
|
164
|
-
|
|
165
|
-
- If `warn` is present: surface it as a non-blocking warning.
|
|
166
|
-
- Save the returned `worktree_id` for reference (if present).
|
|
167
|
-
|
|
168
|
-
The CLI atomically writes the `.codebyplan/` directory with per-concern config stubs and registers the worktree in the CodeByPlan database. The `.codebyplan/device.local.json` file is created by `npx codebyplan setup` on the device (gitignored). The `worktree_id` is never COMMITTED; it may be cached per-device in the gitignored `.codebyplan/worktree.local.json` (branch-keyed, re-derivable via `codebyplan resolve-worktree --cache`), otherwise resolved at runtime from the `(device_id, repo path, branch)` tuple via `npx codebyplan resolve-worktree`.
|
|
169
|
-
|
|
170
|
-
No need to mark as `skip-worktree` — the committed files are merge-safe per CHK-108 and CHK-120.
|
|
171
|
-
|
|
172
|
-
### Step 10: Show Result
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
## Worktree Created
|
|
176
|
-
|
|
177
|
-
**Branch**: [branch-name]
|
|
178
|
-
**Path**: [worktree-path]
|
|
179
|
-
**Main repo**: [main-repo-path]
|
|
180
|
-
**Base**: origin/[production] ([sha])
|
|
181
|
-
**CodeByPlan**: Registered (worktree ID: [id])
|
|
182
|
-
|
|
183
|
-
### Setup
|
|
184
|
-
- MCP: connected (remote endpoint, OAuth Bearer)
|
|
185
|
-
- `.claude/` files: full copy on disk (rules, skills, hooks, agents, context)
|
|
186
|
-
|
|
187
|
-
### Next Steps
|
|
188
|
-
- Open `[worktree-path]` in your editor
|
|
189
|
-
- Run `/cbp-session-start` to begin working
|
|
190
|
-
- Checkpoints can be assigned to this worktree via `worktree_id`
|
|
191
|
-
|
|
192
|
-
### Existing Worktrees
|
|
193
|
-
[output of git worktree list]
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Integration
|
|
197
|
-
|
|
198
|
-
- **Related**: `/cbp-git-worktree-remove` (cleanup and deregister)
|
|
199
|
-
- **CLI**: `codebyplan worktree create <name> --path <abs>` (Step 9 — writes `.codebyplan/` config and registers worktree)
|