@reservine/dx 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-plugin/marketplace.json +22 -0
- package/README.md +303 -0
- package/bin/cli.ts +549 -0
- package/package.json +26 -0
- package/plugins/reservine-dx/.claude-plugin/plugin.json +8 -0
- package/plugins/reservine-dx/commands/cherry-pick-pr.md +221 -0
- package/plugins/reservine-dx/commands/cleanup.md +297 -0
- package/plugins/reservine-dx/commands/commit.md +118 -0
- package/plugins/reservine-dx/docker/worktree/docker-compose.isolated.template.yaml +144 -0
- package/plugins/reservine-dx/docker/worktree/seed-snapshot.sh +74 -0
- package/plugins/reservine-dx/scripts/_core.sh +330 -0
- package/plugins/reservine-dx/scripts/setup-worktree-be.sh +501 -0
- package/plugins/reservine-dx/scripts/setup-worktree-fe.sh +244 -0
- package/plugins/reservine-dx/scripts/setup-worktree.sh +59 -0
- package/plugins/reservine-dx/skills/cross-plan/SKILL.md +339 -0
- package/plugins/reservine-dx/skills/implement-plan/SKILL.md +512 -0
- package/plugins/reservine-dx/skills/implement-plan/references/plugin-contract.md +82 -0
- package/plugins/reservine-dx/skills/new-feature-planning/SKILL.md +544 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Reservine-DX — Angular Frontend Worktree Setup
|
|
4
|
+
#
|
|
5
|
+
# Sets up a git worktree for the Reservine Angular frontend:
|
|
6
|
+
# - Symlinks (or copies) node_modules from the main repo
|
|
7
|
+
# - Copies SSL certs for the dev server
|
|
8
|
+
# - Copies proxy.conf.json for backend API proxying
|
|
9
|
+
# - Copies .env / .env.local
|
|
10
|
+
# - Suggests a dev server port (main uses 1111)
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# ./setup-worktree-fe.sh [OPTIONS]
|
|
14
|
+
#
|
|
15
|
+
# Options:
|
|
16
|
+
# --plan=<name> Search .claude/plans/ for matching file, extract branch name
|
|
17
|
+
# --branch=<name> Create and checkout this branch
|
|
18
|
+
# --base=<branch> Base branch for PR (default: main)
|
|
19
|
+
# --no-dep-link Run bun install instead of symlinking node_modules
|
|
20
|
+
# -h, --help Show this help message
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
# Source shared core
|
|
26
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
27
|
+
source "$SCRIPTS_DIR/_core.sh"
|
|
28
|
+
|
|
29
|
+
# ─── Defaults ───────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
DEP_LINK=true
|
|
32
|
+
BRANCH_NAME=""
|
|
33
|
+
BASE_BRANCH="main"
|
|
34
|
+
PLAN_PATTERN=""
|
|
35
|
+
|
|
36
|
+
# ─── Help ───────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
show_help() {
|
|
39
|
+
cat << 'EOF'
|
|
40
|
+
Reservine-DX — Angular Frontend Worktree Setup
|
|
41
|
+
|
|
42
|
+
Usage: ./setup-worktree-fe.sh [OPTIONS]
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--plan=<name> Search .claude/plans/ for matching file, extract branch name
|
|
46
|
+
--branch=<name> Create and checkout this branch
|
|
47
|
+
--base=<branch> Base branch for PR validation (default: main)
|
|
48
|
+
--no-dep-link Run bun install instead of symlinking node_modules
|
|
49
|
+
-h, --help Show this help message
|
|
50
|
+
|
|
51
|
+
Environment Variables:
|
|
52
|
+
ROOT_WORKTREE_PATH Path to main repo (auto-detected from git worktree list)
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
# Simple setup — auto-detects everything
|
|
56
|
+
./setup-worktree-fe.sh
|
|
57
|
+
|
|
58
|
+
# Setup with explicit branch
|
|
59
|
+
./setup-worktree-fe.sh --branch=feat/my-feature
|
|
60
|
+
|
|
61
|
+
# Install node_modules instead of symlinking
|
|
62
|
+
./setup-worktree-fe.sh --no-dep-link
|
|
63
|
+
|
|
64
|
+
# Setup from a plan file
|
|
65
|
+
./setup-worktree-fe.sh --plan=seo-configurator
|
|
66
|
+
EOF
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ─── Parse arguments ────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
while [[ $# -gt 0 ]]; do
|
|
72
|
+
case $1 in
|
|
73
|
+
--plan=*) PLAN_PATTERN="${1#*=}"; shift ;;
|
|
74
|
+
--branch=*) BRANCH_NAME="${1#*=}"; shift ;;
|
|
75
|
+
--base=*) BASE_BRANCH="${1#*=}"; shift ;;
|
|
76
|
+
--no-dep-link) DEP_LINK=false; shift ;;
|
|
77
|
+
--log) LOG_FILE="$(pwd)/setup.log"; shift ;;
|
|
78
|
+
--log=*) LOG_FILE="${1#*=}"; shift ;;
|
|
79
|
+
-h|--help) show_help; exit 0 ;;
|
|
80
|
+
*) error "Unknown option: $1"; show_help; exit 1 ;;
|
|
81
|
+
esac
|
|
82
|
+
done
|
|
83
|
+
|
|
84
|
+
init_log "$@"
|
|
85
|
+
|
|
86
|
+
# ─── Plan resolution ────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
if [[ -n "$PLAN_PATTERN" ]]; then
|
|
89
|
+
resolve_plan_file "$PLAN_PATTERN"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ─── Pre-flight ─────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
resolve_root_worktree_path
|
|
95
|
+
preflight_check
|
|
96
|
+
|
|
97
|
+
info "Stack: Angular (bun)"
|
|
98
|
+
|
|
99
|
+
# ─── Branch setup ────────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
if [[ -n "$BRANCH_NAME" ]]; then
|
|
102
|
+
setup_branch "$BRANCH_NAME" "$BASE_BRANCH"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# ─── Environment setup ──────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
if [[ "$IS_CLOUD" == true ]]; then
|
|
108
|
+
info "Cloud environment — skipping local setup"
|
|
109
|
+
else
|
|
110
|
+
echo "⚙️ Angular Environment Setup"
|
|
111
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
112
|
+
|
|
113
|
+
# ── node_modules ──
|
|
114
|
+
|
|
115
|
+
if [[ "$DEP_LINK" == true ]]; then
|
|
116
|
+
if [[ -d "node_modules" ]] && [[ ! -L "node_modules" ]]; then
|
|
117
|
+
warn "node_modules/ is a directory — removing to create symlink"
|
|
118
|
+
rm -rf node_modules
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
if [[ -L "node_modules" ]]; then
|
|
122
|
+
info "node_modules/ symlink already exists → $(readlink node_modules)"
|
|
123
|
+
elif [[ -d "${ROOT_WORKTREE_PATH}/node_modules" ]]; then
|
|
124
|
+
ln -s "${ROOT_WORKTREE_PATH}/node_modules" node_modules
|
|
125
|
+
success "Symlinked node_modules/ → main repo"
|
|
126
|
+
else
|
|
127
|
+
warn "Main repo has no node_modules/ — running bun install"
|
|
128
|
+
bun install
|
|
129
|
+
success "Installed node_modules/"
|
|
130
|
+
fi
|
|
131
|
+
else
|
|
132
|
+
if [[ -d "node_modules" ]] && [[ ! -L "node_modules" ]]; then
|
|
133
|
+
info "node_modules/ directory already exists (local copy)"
|
|
134
|
+
else
|
|
135
|
+
[[ -L "node_modules" ]] && rm node_modules
|
|
136
|
+
if [[ -d "${ROOT_WORKTREE_PATH}/node_modules" ]]; then
|
|
137
|
+
info "Copying node_modules/ from main repo (this may take a moment)..."
|
|
138
|
+
cp -R "${ROOT_WORKTREE_PATH}/node_modules" node_modules
|
|
139
|
+
success "Copied node_modules/ (local copy)"
|
|
140
|
+
else
|
|
141
|
+
bun install
|
|
142
|
+
success "Installed node_modules/"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# ── SSL certs ──
|
|
148
|
+
|
|
149
|
+
for cert in localhost.pem localhost-key.pem; do
|
|
150
|
+
if [[ ! -f "$cert" ]] && [[ -f "${ROOT_WORKTREE_PATH}/$cert" ]]; then
|
|
151
|
+
cp "${ROOT_WORKTREE_PATH}/$cert" .
|
|
152
|
+
success "Copied $cert"
|
|
153
|
+
fi
|
|
154
|
+
done
|
|
155
|
+
|
|
156
|
+
# ── proxy.conf.json ──
|
|
157
|
+
# Priority: 1) already exists (generated by BE --isolated), 2) detect running BE stack, 3) copy from main
|
|
158
|
+
|
|
159
|
+
if [[ -f "proxy.conf.json" ]]; then
|
|
160
|
+
info "proxy.conf.json already exists (generated by BE --isolated)"
|
|
161
|
+
else
|
|
162
|
+
# Check if a BE isolated stack is running for this branch
|
|
163
|
+
BRANCH_SLUG=$(get_branch_slug)
|
|
164
|
+
BE_APP_PORT=$(docker ps --filter "name=wt-${BRANCH_SLUG}-app" --format "{{.Ports}}" 2>/dev/null \
|
|
165
|
+
| grep -oE '0\.0\.0\.0:[0-9]+->80' | head -1 | cut -d: -f2 | cut -d- -f1)
|
|
166
|
+
if [[ -n "$BE_APP_PORT" ]]; then
|
|
167
|
+
cat > proxy.conf.json <<PROXY_EOF
|
|
168
|
+
{
|
|
169
|
+
"/api": { "target": "http://localhost:${BE_APP_PORT}", "secure": false, "changeOrigin": true },
|
|
170
|
+
"/sanctum": { "target": "http://localhost:${BE_APP_PORT}", "secure": false, "changeOrigin": true }
|
|
171
|
+
}
|
|
172
|
+
PROXY_EOF
|
|
173
|
+
success "Generated proxy.conf.json → isolated BE on :${BE_APP_PORT}"
|
|
174
|
+
elif [[ -f "${ROOT_WORKTREE_PATH}/proxy.conf.json" ]]; then
|
|
175
|
+
cp "${ROOT_WORKTREE_PATH}/proxy.conf.json" .
|
|
176
|
+
success "Copied proxy.conf.json from main repo"
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# ── .env files ──
|
|
181
|
+
|
|
182
|
+
for envfile in .env .env.local; do
|
|
183
|
+
if [[ ! -f "$envfile" ]] && [[ -f "${ROOT_WORKTREE_PATH}/$envfile" ]]; then
|
|
184
|
+
cp "${ROOT_WORKTREE_PATH}/$envfile" .
|
|
185
|
+
success "Copied $envfile"
|
|
186
|
+
fi
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
# ── .claude/config.local ──
|
|
190
|
+
|
|
191
|
+
if [[ ! -f ".claude/config.local" ]] && [[ -f "${ROOT_WORKTREE_PATH}/.claude/config.local" ]]; then
|
|
192
|
+
mkdir -p .claude
|
|
193
|
+
cp "${ROOT_WORKTREE_PATH}/.claude/config.local" .claude/config.local
|
|
194
|
+
success "Copied .claude/config.local"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
echo ""
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# ─── Final output ───────────────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
echo "✨ Worktree setup complete!"
|
|
203
|
+
echo ""
|
|
204
|
+
|
|
205
|
+
if [[ "$IS_CLOUD" == false ]]; then
|
|
206
|
+
# Find first free port in the worktree range (main uses 1111)
|
|
207
|
+
SUGGESTED_PORT=""
|
|
208
|
+
for port in 4200 4201 4202 4203 4204 4205; do
|
|
209
|
+
if ! port_in_use "$port"; then
|
|
210
|
+
SUGGESTED_PORT=$port
|
|
211
|
+
break
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
SUGGESTED_PORT=${SUGGESTED_PORT:-4200} # fallback if all in use
|
|
215
|
+
|
|
216
|
+
# Write worktree port to config.local so Playwright MCP can auto-connect
|
|
217
|
+
mkdir -p .claude
|
|
218
|
+
if grep -q "^MCP_WORKTREE_PORT=" .claude/config.local 2>/dev/null; then
|
|
219
|
+
sed -i.bak "s/^MCP_WORKTREE_PORT=.*/MCP_WORKTREE_PORT=$SUGGESTED_PORT/" .claude/config.local
|
|
220
|
+
rm -f .claude/config.local.bak
|
|
221
|
+
else
|
|
222
|
+
echo "" >> .claude/config.local 2>/dev/null || true
|
|
223
|
+
echo "# Worktree dev server port (auto-detected by setup script)" >> .claude/config.local
|
|
224
|
+
echo "MCP_WORKTREE_PORT=$SUGGESTED_PORT" >> .claude/config.local
|
|
225
|
+
fi
|
|
226
|
+
success "Set MCP_WORKTREE_PORT=$SUGGESTED_PORT in .claude/config.local"
|
|
227
|
+
|
|
228
|
+
echo "📦 Angular Worktree Ready"
|
|
229
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
230
|
+
echo ""
|
|
231
|
+
echo " Start dev server (main uses port 1111):"
|
|
232
|
+
echo " nx serve reservine --port $SUGGESTED_PORT"
|
|
233
|
+
echo ""
|
|
234
|
+
echo " With SSL:"
|
|
235
|
+
echo " nx serve reservine --port $SUGGESTED_PORT --ssl"
|
|
236
|
+
echo ""
|
|
237
|
+
if [[ -f "proxy.conf.json" ]]; then
|
|
238
|
+
echo " With local backend proxy:"
|
|
239
|
+
echo " nx serve reservine --port $SUGGESTED_PORT --proxy-config=proxy.conf.json"
|
|
240
|
+
echo ""
|
|
241
|
+
fi
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
print_state "node_modules" "$BRANCH_NAME"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Reservine-DX — Worktree Setup (entry point)
|
|
4
|
+
#
|
|
5
|
+
# Auto-detects the current stack (Angular or Laravel) and delegates to the
|
|
6
|
+
# appropriate stack-specific setup script.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# ./setup-worktree.sh [OPTIONS]
|
|
10
|
+
#
|
|
11
|
+
# The script will detect:
|
|
12
|
+
# - angular.json / nx.json → runs setup-worktree-fe.sh
|
|
13
|
+
# - artisan / composer.json → runs setup-worktree-be.sh
|
|
14
|
+
#
|
|
15
|
+
# All options are forwarded to the stack-specific script.
|
|
16
|
+
# See setup-worktree-fe.sh --help or setup-worktree-be.sh --help for details.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
22
|
+
|
|
23
|
+
# Quick help
|
|
24
|
+
if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
|
|
25
|
+
cat << 'EOF'
|
|
26
|
+
Reservine-DX — Worktree Setup
|
|
27
|
+
|
|
28
|
+
Auto-detects the stack and runs the appropriate setup script.
|
|
29
|
+
|
|
30
|
+
Usage: ./setup-worktree.sh [OPTIONS]
|
|
31
|
+
|
|
32
|
+
Stack detection:
|
|
33
|
+
angular.json / nx.json → Angular (setup-worktree-fe.sh)
|
|
34
|
+
artisan / composer.json → Laravel (setup-worktree-be.sh)
|
|
35
|
+
|
|
36
|
+
You can also call the stack-specific script directly:
|
|
37
|
+
./setup-worktree-fe.sh [OPTIONS]
|
|
38
|
+
./setup-worktree-be.sh [OPTIONS]
|
|
39
|
+
|
|
40
|
+
Run with --help on a specific script for its options:
|
|
41
|
+
./setup-worktree-fe.sh --help
|
|
42
|
+
./setup-worktree-be.sh --help
|
|
43
|
+
EOF
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Detect stack
|
|
48
|
+
if [[ -f "angular.json" ]] || [[ -f "nx.json" ]]; then
|
|
49
|
+
exec "$SCRIPTS_DIR/setup-worktree-fe.sh" "$@"
|
|
50
|
+
elif [[ -f "artisan" ]] || [[ -f "composer.json" ]]; then
|
|
51
|
+
exec "$SCRIPTS_DIR/setup-worktree-be.sh" "$@"
|
|
52
|
+
else
|
|
53
|
+
echo "❌ Could not detect stack (no angular.json, nx.json, artisan, or composer.json found)" >&2
|
|
54
|
+
echo "" >&2
|
|
55
|
+
echo " Run the stack-specific script directly:" >&2
|
|
56
|
+
echo " $SCRIPTS_DIR/setup-worktree-fe.sh [OPTIONS] # Angular" >&2
|
|
57
|
+
echo " $SCRIPTS_DIR/setup-worktree-be.sh [OPTIONS] # Laravel" >&2
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reservine-dx:cross-plan
|
|
3
|
+
description: >
|
|
4
|
+
Create an intent file and linked GitHub issue in the sibling repo when a feature requires
|
|
5
|
+
cross-repo changes. Auto-detects direction (FE→BE or BE→FE) and creates the appropriate
|
|
6
|
+
intent file template with skill references, GitHub issue tracking, sub-issue linking,
|
|
7
|
+
and milestone sync. Works from either repo.
|
|
8
|
+
Triggers: "cross-plan", "create intent file", "needs backend/frontend changes".
|
|
9
|
+
argument-hint: [feature description]
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- Read
|
|
12
|
+
- Write
|
|
13
|
+
- Bash
|
|
14
|
+
- AskUserQuestion
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Cross-Project Plan (Bidirectional)
|
|
18
|
+
|
|
19
|
+
Create an intent file and linked GitHub issue in the sibling repo when a feature requires
|
|
20
|
+
cross-repo changes. Auto-detects whether you're in the FE or BE repo and creates the
|
|
21
|
+
appropriate direction-specific artifacts.
|
|
22
|
+
|
|
23
|
+
## Step 1: Gather Context & Detect Direction
|
|
24
|
+
|
|
25
|
+
Read the project config to resolve paths and repo identifiers:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cat .claude/config 2>/dev/null
|
|
29
|
+
cat .claude/config.local 2>/dev/null
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Auto-detect direction:**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
if [[ -f "angular.json" ]] || [[ -f "nx.json" ]]; then
|
|
36
|
+
DIRECTION="FE_TO_BE"
|
|
37
|
+
CURRENT_REPO="FE"
|
|
38
|
+
CURRENT_GITHUB_REPO=$GITHUB_REPO # from .claude/config
|
|
39
|
+
TARGET_DIR=$(grep 'BACKEND_DIR' .claude/config | cut -d= -f2)
|
|
40
|
+
TARGET_DIR_ABS=$(grep 'BACKEND_DIR_ABSOLUTE' .claude/config.local 2>/dev/null | cut -d= -f2)
|
|
41
|
+
[[ -n "$TARGET_DIR_ABS" ]] && TARGET_DIR="$TARGET_DIR_ABS"
|
|
42
|
+
else
|
|
43
|
+
DIRECTION="BE_TO_FE"
|
|
44
|
+
CURRENT_REPO="BE"
|
|
45
|
+
CURRENT_GITHUB_REPO=$GITHUB_REPO
|
|
46
|
+
TARGET_DIR=$(grep 'FRONTEND_DIR' .claude/config | cut -d= -f2)
|
|
47
|
+
TARGET_DIR_ABS=$(grep 'FRONTEND_DIR_ABSOLUTE' .claude/config.local 2>/dev/null | cut -d= -f2)
|
|
48
|
+
[[ -n "$TARGET_DIR_ABS" ]] && TARGET_DIR="$TARGET_DIR_ABS"
|
|
49
|
+
fi
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then read the target repo config:
|
|
53
|
+
```bash
|
|
54
|
+
cat $TARGET_DIR/.claude/config 2>/dev/null
|
|
55
|
+
```
|
|
56
|
+
Extract `GITHUB_REPO` from the target config → `TARGET_GITHUB_REPO`.
|
|
57
|
+
|
|
58
|
+
**Fallback defaults:**
|
|
59
|
+
- FE repo: `LEFTEQ/reservine`
|
|
60
|
+
- BE repo: `genesiscz/ReservineBack`
|
|
61
|
+
|
|
62
|
+
### Resolve current issue number
|
|
63
|
+
|
|
64
|
+
Look for the current issue number from these sources (in priority order):
|
|
65
|
+
1. Conversation context — if working on an issue (e.g., `implement-plan` invoked with `#259`)
|
|
66
|
+
2. Current branch name — extract issue number from patterns like `feat/social-login-259`
|
|
67
|
+
3. If no issue is found, proceed without cross-linking (intent file + standalone issue)
|
|
68
|
+
|
|
69
|
+
### Resolve feature name
|
|
70
|
+
|
|
71
|
+
If `$ARGUMENTS` is provided, use it as the feature description. Otherwise, infer from conversation context or ask the user:
|
|
72
|
+
|
|
73
|
+
1. **Feature name** — short kebab-case identifier (e.g., `sms-confirmation-toggle`)
|
|
74
|
+
2. **What the target repo needs to do** — endpoints/pages, data changes, components
|
|
75
|
+
3. **Why** — business context
|
|
76
|
+
|
|
77
|
+
## Step 2: Write the Intent File
|
|
78
|
+
|
|
79
|
+
Create the file at: `<TARGET_DIR>/.claude/plans/future/<feature>.intent.md`
|
|
80
|
+
|
|
81
|
+
### FE→BE template (API-focused)
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
# <Feature Name> — Intent
|
|
85
|
+
|
|
86
|
+
> **Auto-invoke these skills when starting work on this plan:**
|
|
87
|
+
> 1. `/reservine-dx:implement-plan` — full end-to-end implementation orchestrator (worktree, tests, PR)
|
|
88
|
+
> 2. `/reservine:api` — API endpoint implementation patterns (migration, model, service, controller, routes, tests)
|
|
89
|
+
|
|
90
|
+
## What we need
|
|
91
|
+
<Business-level description of what the backend must provide>
|
|
92
|
+
|
|
93
|
+
## API changes needed
|
|
94
|
+
<Endpoints to add/modify, request/response shapes, HTTP methods>
|
|
95
|
+
|
|
96
|
+
## Permissions & authorization
|
|
97
|
+
<Required roles, new permissions, middleware considerations>
|
|
98
|
+
|
|
99
|
+
## Why
|
|
100
|
+
<Business context — why this change matters>
|
|
101
|
+
|
|
102
|
+
## FE context
|
|
103
|
+
<What the frontend is doing, which components/services will consume the API>
|
|
104
|
+
|
|
105
|
+
## Data shape expected by FE
|
|
106
|
+
<TypeScript interfaces or JSON examples the frontend expects>
|
|
107
|
+
|
|
108
|
+
## Tracking
|
|
109
|
+
- FE issue: <FE_GITHUB_REPO>#<current-issue>
|
|
110
|
+
- BE issue: <BE_GITHUB_REPO>#<be-issue>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### BE→FE template (UI-focused)
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
# <Feature Name> — Intent
|
|
117
|
+
|
|
118
|
+
> **Auto-invoke these skills when starting work on this plan:**
|
|
119
|
+
> 1. `/reservine-dx:implement-plan` — full end-to-end implementation orchestrator (design critique, questionnaire, worktree, E2E tests, PR)
|
|
120
|
+
> 2. `/reservine:design` — UI design & component composition
|
|
121
|
+
> 3. `/reservine:play` — Playwright MCP browser tools for E2E testing
|
|
122
|
+
> 4. _(optional, when creating/modifying forms)_ `/reservine:form-builder`
|
|
123
|
+
|
|
124
|
+
## What we need
|
|
125
|
+
<Business-level description of what the frontend must provide>
|
|
126
|
+
|
|
127
|
+
## UI changes needed
|
|
128
|
+
<Pages/components to add/modify, expected UX flow>
|
|
129
|
+
|
|
130
|
+
## Data shape from BE
|
|
131
|
+
<What the API returns — response format, TypeScript types, endpoint paths>
|
|
132
|
+
|
|
133
|
+
## Why
|
|
134
|
+
<Business context — why this change matters>
|
|
135
|
+
|
|
136
|
+
## BE context
|
|
137
|
+
<What the backend is doing, which endpoints are available, auth requirements>
|
|
138
|
+
|
|
139
|
+
## Tracking
|
|
140
|
+
- FE issue: <FE_GITHUB_REPO>#<fe-issue>
|
|
141
|
+
- BE issue: <BE_GITHUB_REPO>#<current-issue>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Note:** The `## Tracking` section uses placeholders initially — update it after the target issue is created (Step 3).
|
|
145
|
+
|
|
146
|
+
## Step 3: Create Target GitHub Issue
|
|
147
|
+
|
|
148
|
+
Create a GitHub issue in the target repo for tracking implementation:
|
|
149
|
+
|
|
150
|
+
### FE→BE: Create BE issue
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
TARGET_ISSUE_URL=$(gh issue create --repo $TARGET_GITHUB_REPO \
|
|
154
|
+
--title "<Feature Title> — Backend" \
|
|
155
|
+
--body "$(cat <<'EOF'
|
|
156
|
+
- [ ] @LEFTEQ read this
|
|
157
|
+
- [ ] @genesiscz read this
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Summary
|
|
162
|
+
<1-3 sentence business-level summary from intent file>
|
|
163
|
+
|
|
164
|
+
## Intent File
|
|
165
|
+
Implementation plan: `<relative-path-to-intent-file>`
|
|
166
|
+
|
|
167
|
+
> Open a session in the BE repo and run:
|
|
168
|
+
> `Implement the feature described in <absolute-path-to-intent-file>`
|
|
169
|
+
|
|
170
|
+
## FE Reference
|
|
171
|
+
Frontend implementation: <FE_GITHUB_REPO>#<current-issue>
|
|
172
|
+
EOF
|
|
173
|
+
)")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### BE→FE: Create FE issue
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
TARGET_ISSUE_URL=$(gh issue create --repo $TARGET_GITHUB_REPO \
|
|
180
|
+
--title "<Feature Title>" \
|
|
181
|
+
--body "$(cat <<'EOF'
|
|
182
|
+
- [ ] @LEFTEQ read this
|
|
183
|
+
- [ ] @genesiscz read this
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Summary
|
|
188
|
+
<1-3 sentence business-level summary from intent file>
|
|
189
|
+
|
|
190
|
+
## Intent File
|
|
191
|
+
Implementation plan: `<relative-path-to-intent-file>`
|
|
192
|
+
|
|
193
|
+
> Open a session in the FE repo and run:
|
|
194
|
+
> `Implement the feature described in <absolute-path-to-intent-file>`
|
|
195
|
+
|
|
196
|
+
## BE Reference
|
|
197
|
+
Backend implementation: <BE_GITHUB_REPO>#<current-issue>
|
|
198
|
+
EOF
|
|
199
|
+
)")
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Extract the target issue number from the returned URL.
|
|
203
|
+
|
|
204
|
+
### Milestone sync
|
|
205
|
+
|
|
206
|
+
If the current issue has a milestone, apply the same milestone to the target issue:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
MILESTONE=$(gh issue view <current-issue> --repo $CURRENT_GITHUB_REPO --json milestone --jq '.milestone.title // empty')
|
|
210
|
+
if [[ -n "$MILESTONE" ]]; then
|
|
211
|
+
gh issue edit <target-issue> --repo $TARGET_GITHUB_REPO --milestone "$MILESTONE"
|
|
212
|
+
fi
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Apply label
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
gh issue edit <target-issue> --repo $TARGET_GITHUB_REPO --add-label "status:planning" 2>/dev/null || true
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Update intent file tracking section
|
|
222
|
+
|
|
223
|
+
Now that the target issue number is known, update the `## Tracking` section in the intent file
|
|
224
|
+
with the actual issue numbers.
|
|
225
|
+
|
|
226
|
+
## Step 4: Link Issues
|
|
227
|
+
|
|
228
|
+
### Sub-issue linking (FE = parent, BE = child)
|
|
229
|
+
|
|
230
|
+
Determine which is the FE issue and which is the BE issue:
|
|
231
|
+
- **FE→BE**: FE issue = `<current-issue>` in `FE_GITHUB_REPO`, BE issue = `<target-issue>` in `BE_GITHUB_REPO`
|
|
232
|
+
- **BE→FE**: FE issue = `<target-issue>` in `FE_GITHUB_REPO`, BE issue = `<current-issue>` in `BE_GITHUB_REPO`
|
|
233
|
+
|
|
234
|
+
FE is always the parent:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Get FE issue node ID
|
|
238
|
+
FE_OWNER=$(echo $FE_GITHUB_REPO | cut -d/ -f1)
|
|
239
|
+
FE_NAME=$(echo $FE_GITHUB_REPO | cut -d/ -f2)
|
|
240
|
+
FE_NODE_ID=$(gh api graphql -f query='
|
|
241
|
+
{ repository(owner:"'"$FE_OWNER"'", name:"'"$FE_NAME"'") {
|
|
242
|
+
issue(number:<fe-issue>) { id }
|
|
243
|
+
} }' --jq '.data.repository.issue.id')
|
|
244
|
+
|
|
245
|
+
# Get BE issue node ID
|
|
246
|
+
BE_OWNER=$(echo $BE_GITHUB_REPO | cut -d/ -f1)
|
|
247
|
+
BE_NAME=$(echo $BE_GITHUB_REPO | cut -d/ -f2)
|
|
248
|
+
BE_NODE_ID=$(gh api graphql -f query='
|
|
249
|
+
{ repository(owner:"'"$BE_OWNER"'", name:"'"$BE_NAME"'") {
|
|
250
|
+
issue(number:<be-issue>) { id }
|
|
251
|
+
} }' --jq '.data.repository.issue.id')
|
|
252
|
+
|
|
253
|
+
# Add BE as sub-issue of FE
|
|
254
|
+
gh api graphql -f query='
|
|
255
|
+
mutation {
|
|
256
|
+
addSubIssue(input: {
|
|
257
|
+
issueId: "'"$FE_NODE_ID"'"
|
|
258
|
+
subIssueId: "'"$BE_NODE_ID"'"
|
|
259
|
+
}) {
|
|
260
|
+
issue { id }
|
|
261
|
+
subIssue { id }
|
|
262
|
+
}
|
|
263
|
+
}'
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If the sub-issue API fails (feature not available), fall back to cross-reference comments only.
|
|
267
|
+
|
|
268
|
+
### Cross-reference comments
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# On current repo issue
|
|
272
|
+
gh issue comment <current-issue> --repo $CURRENT_GITHUB_REPO \
|
|
273
|
+
--body "**Target Issue:** $TARGET_GITHUB_REPO#<target-issue>
|
|
274
|
+
Frontend implementation tracked here. Intent file: \`<path-to-intent-file>\`"
|
|
275
|
+
|
|
276
|
+
# On target repo issue
|
|
277
|
+
gh issue comment <target-issue> --repo $TARGET_GITHUB_REPO \
|
|
278
|
+
--body "**Source Issue:** $CURRENT_GITHUB_REPO#<current-issue>
|
|
279
|
+
Linked as sub-issue for implementation tracking."
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### No current issue available
|
|
283
|
+
|
|
284
|
+
If no current issue was found in Step 1:
|
|
285
|
+
- Skip sub-issue linking entirely
|
|
286
|
+
- Skip cross-reference comments
|
|
287
|
+
- Create the target issue without the reference section
|
|
288
|
+
- Note in the output that no source issue was linked
|
|
289
|
+
|
|
290
|
+
## Step 5: Plan Directory Structure
|
|
291
|
+
|
|
292
|
+
The target repo uses this directory layout:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
.claude/plans/
|
|
296
|
+
├── future/ # ← you write here
|
|
297
|
+
│ └── <feature>.intent.md
|
|
298
|
+
├── active/ # ← receiving AI moves here when starting work
|
|
299
|
+
│ └── <feature>/
|
|
300
|
+
│ ├── <feature>.intent.md
|
|
301
|
+
│ └── <feature>.technical.md
|
|
302
|
+
└── finished/ # ← after implementation
|
|
303
|
+
└── <feature>/
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Step 6: Output the Handoff
|
|
307
|
+
|
|
308
|
+
After writing the intent file and creating/linking issues, output this EXACT format (replace placeholders):
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
**Cross-plan ready.**
|
|
313
|
+
|
|
314
|
+
Target issue created: `<TARGET_GITHUB_REPO>#<target-issue>` — <link-to-target-issue>
|
|
315
|
+
Linked to source issue: `<CURRENT_GITHUB_REPO>#<current-issue>`
|
|
316
|
+
|
|
317
|
+
Intent file written to:
|
|
318
|
+
`<full absolute path to the intent file>`
|
|
319
|
+
|
|
320
|
+
Open a new session in **<target repo name>** and paste:
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
Implement the feature described in <full absolute path to intent file>. Read the intent file — it contains the requirements and lists which skills to auto-invoke before writing code. Create a technical plan first, then implement.
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
If no source issue was linked, replace the "Linked to" line with:
|
|
329
|
+
`No source issue linked (not detected in conversation context).`
|
|
330
|
+
|
|
331
|
+
## Rules
|
|
332
|
+
|
|
333
|
+
- Intent files are **business-level** — describe WHAT is needed, not HOW to build it
|
|
334
|
+
- Never write implementation details in the intent file — that's the receiving repo's job
|
|
335
|
+
- Always create the target GitHub issue — it's the tracking ticket for the cross-repo work
|
|
336
|
+
- Always attempt to link issues when a source issue number is available
|
|
337
|
+
- FE is always the parent in sub-issue relationships
|
|
338
|
+
- Always use the exact handoff format above — the user copies it into the next session
|
|
339
|
+
- The receiving AI reads the intent file, invokes the listed skills, and handles the rest
|