@lumenflow/cli 2.5.1 → 2.7.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/dist/__tests__/gates-config.test.js +304 -0
- package/dist/__tests__/wu-proto.test.js +97 -0
- package/dist/gates.js +64 -15
- package/dist/init.js +13 -1
- package/dist/wu-proto.js +330 -0
- package/package.json +7 -6
- package/templates/core/.husky/pre-commit.template +93 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +27 -0
- package/templates/core/ai/onboarding/rapid-prototyping.md +143 -0
- package/templates/core/ai/onboarding/starting-prompt.md.template +3 -3
- package/templates/vendors/claude/.claude/CLAUDE.md.template +25 -0
- package/templates/vendors/claude/.claude/hooks/enforce-worktree.sh +135 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# enforce-worktree.sh
|
|
4
|
+
#
|
|
5
|
+
# Claude PreToolUse hook that blocks Write/Edit operations on main branch.
|
|
6
|
+
#
|
|
7
|
+
# This hook enforces worktree discipline for Claude Code specifically,
|
|
8
|
+
# complementing the git pre-commit hook for stronger enforcement.
|
|
9
|
+
#
|
|
10
|
+
# Exit codes:
|
|
11
|
+
# 0 = Allow operation
|
|
12
|
+
# 2 = Block operation (stderr shown to Claude as guidance)
|
|
13
|
+
#
|
|
14
|
+
# Security: Fail-open design for this hook (branches can't always be detected)
|
|
15
|
+
# - If branch detection fails, allow operation (git hooks will catch it)
|
|
16
|
+
# - If JSON parse fails, allow operation (defensive, log warning)
|
|
17
|
+
#
|
|
18
|
+
# Blocking conditions:
|
|
19
|
+
# - Current branch is main or master
|
|
20
|
+
# - Tool is Write or Edit
|
|
21
|
+
# - Target file is in the main repo (not a worktree)
|
|
22
|
+
#
|
|
23
|
+
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
# Derive repo paths from CLAUDE_PROJECT_DIR
|
|
27
|
+
if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
|
|
28
|
+
MAIN_REPO_PATH="$CLAUDE_PROJECT_DIR"
|
|
29
|
+
else
|
|
30
|
+
MAIN_REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
31
|
+
if [[ -z "$MAIN_REPO_PATH" ]]; then
|
|
32
|
+
# Not in a git repo - allow operation
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Check if we're on main/master branch
|
|
38
|
+
CURRENT_BRANCH=$(git -C "$MAIN_REPO_PATH" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
39
|
+
|
|
40
|
+
# If branch detection fails, fail-open (git hooks will catch issues)
|
|
41
|
+
if [[ -z "$CURRENT_BRANCH" ]]; then
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Allow operations on non-main branches
|
|
46
|
+
case "$CURRENT_BRANCH" in
|
|
47
|
+
main|master)
|
|
48
|
+
# Continue to check tool type
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
# Not on main/master - allow
|
|
52
|
+
exit 0
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
# Read JSON input from stdin
|
|
57
|
+
INPUT=$(cat)
|
|
58
|
+
|
|
59
|
+
# If no input, fail-open (defensive)
|
|
60
|
+
if [[ -z "$INPUT" ]]; then
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Parse JSON with Python to extract tool_name and file_path
|
|
65
|
+
PARSE_RESULT=$(python3 -c "
|
|
66
|
+
import json
|
|
67
|
+
import sys
|
|
68
|
+
try:
|
|
69
|
+
data = json.loads('''$INPUT''')
|
|
70
|
+
tool_name = data.get('tool_name', '')
|
|
71
|
+
tool_input = data.get('tool_input', {})
|
|
72
|
+
if not isinstance(tool_input, dict):
|
|
73
|
+
tool_input = {}
|
|
74
|
+
file_path = tool_input.get('file_path', '')
|
|
75
|
+
print('OK')
|
|
76
|
+
print(tool_name if tool_name else '')
|
|
77
|
+
print(file_path if file_path else '')
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print('ERROR')
|
|
80
|
+
print(str(e))
|
|
81
|
+
print('')
|
|
82
|
+
" 2>&1)
|
|
83
|
+
|
|
84
|
+
# Parse the result
|
|
85
|
+
PARSE_STATUS=$(echo "$PARSE_RESULT" | head -1)
|
|
86
|
+
TOOL_NAME=$(echo "$PARSE_RESULT" | sed -n '2p')
|
|
87
|
+
FILE_PATH=$(echo "$PARSE_RESULT" | sed -n '3p')
|
|
88
|
+
|
|
89
|
+
# If parse failed, fail-open (defensive)
|
|
90
|
+
if [[ "$PARSE_STATUS" != "OK" ]]; then
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Only block Write and Edit tools
|
|
95
|
+
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# Check if file path is in a worktree (allowed) or main repo (blocked)
|
|
100
|
+
WORKTREES_DIR="${MAIN_REPO_PATH}/worktrees"
|
|
101
|
+
|
|
102
|
+
if [[ -n "$FILE_PATH" ]]; then
|
|
103
|
+
RESOLVED_PATH=$(realpath -m "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
|
|
104
|
+
|
|
105
|
+
# Allow if path is inside a worktree
|
|
106
|
+
if [[ "$RESOLVED_PATH" == "${WORKTREES_DIR}/"* ]]; then
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Block: We're on main/master and trying to Write/Edit outside a worktree
|
|
112
|
+
echo "" >&2
|
|
113
|
+
echo "=== LumenFlow Worktree Enforcement ===" >&2
|
|
114
|
+
echo "" >&2
|
|
115
|
+
echo "BLOCKED: ${TOOL_NAME} operation on main branch" >&2
|
|
116
|
+
echo "" >&2
|
|
117
|
+
echo "You are on the '${CURRENT_BRANCH}' branch. Direct edits to main are not allowed." >&2
|
|
118
|
+
echo "" >&2
|
|
119
|
+
echo "WHAT TO DO:" >&2
|
|
120
|
+
echo " 1. Claim a WU to create a worktree:" >&2
|
|
121
|
+
echo " pnpm wu:claim --id WU-XXXX --lane \"<Lane>\"" >&2
|
|
122
|
+
echo "" >&2
|
|
123
|
+
echo " 2. Move to the worktree:" >&2
|
|
124
|
+
echo " cd worktrees/<lane>-wu-xxxx" >&2
|
|
125
|
+
echo "" >&2
|
|
126
|
+
echo " 3. Make your edits in the worktree" >&2
|
|
127
|
+
echo "" >&2
|
|
128
|
+
echo "WHY THIS MATTERS:" >&2
|
|
129
|
+
echo " - Worktrees isolate your changes from other work" >&2
|
|
130
|
+
echo " - All changes are tracked through the WU workflow" >&2
|
|
131
|
+
echo " - Parallel work across lanes stays independent" >&2
|
|
132
|
+
echo "" >&2
|
|
133
|
+
echo "See: LUMENFLOW.md for complete workflow documentation" >&2
|
|
134
|
+
echo "========================================" >&2
|
|
135
|
+
exit 2
|