lathe-cli 1.1.0 → 1.3.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/README.md +46 -15
- package/bin/_lathe-task.sh +213 -0
- package/bin/check-deps.sh +1 -1
- package/bin/lathe +12 -7
- package/bin/lathe-init +64 -43
- package/bin/lathe-meta +1 -1
- package/bin/lathe-plan +117 -0
- package/bin/lathe-process +141 -59
- package/bin/lathe-sync +1 -1
- package/bin/lathe-target +2 -2
- package/bin/lathe-watch +126 -0
- package/package.json +2 -2
- package/template/develop/bin/sync.sh +68 -38
- package/template/develop/harness/CLAUDE.md +47 -28
- package/template/develop/harness/hooks/commit-runs.sh +2 -2
- package/template/develop/harness/hooks/copy_transcript.sh +2 -2
- package/template/develop/harness/hooks/log.sh +3 -3
- package/template/{meta-overlay → develop/harness}/meta/CLAUDE.md +4 -4
- package/template/{meta-overlay/meta/.claude → develop/harness/meta}/skills/log-reading/SKILL.md +1 -1
- package/template/develop/harness/plan_template.html +2 -2
- package/template/develop/harness/skills/planning/SKILL.md +9 -5
- package/template/git-hooks/post-merge +15 -8
- package/template/helper-claude/.claude/CLAUDE.md +28 -0
- package/template/helper-claude/.claude/skills/lathe-cli/SKILL.md +37 -14
- /package/template/{meta-overlay/meta/.claude → develop/harness/meta}/settings.json +0 -0
- /package/template/{meta-overlay/meta/.claude → develop/harness/meta}/skills/improvement-recording/SKILL.md +0 -0
- /package/template/{meta-overlay → develop}/improvements/.gitkeep +0 -0
package/bin/lathe-plan
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lathe plan pr <pr#> — prepare a task and run target planning only.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
: "${LATHE_HOME:?must be set by lathe dispatcher}"
|
|
6
|
+
source "$LATHE_HOME/bin/_lathe-lib.sh"
|
|
7
|
+
source "$LATHE_HOME/bin/_lathe-task.sh"
|
|
8
|
+
|
|
9
|
+
usage() {
|
|
10
|
+
cat >&2 <<EOF
|
|
11
|
+
Usage: lathe plan [--print|--manual-prompt|--no-launch] pr <pr#>
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--print Run Claude non-interactively with --print and a plan-only prompt.
|
|
15
|
+
Used by lathe watch.
|
|
16
|
+
--manual-prompt Launch Claude without submitting the initial prompt.
|
|
17
|
+
The prompt is printed for manual paste.
|
|
18
|
+
--no-launch Prepare tasks/<pr#>/ and print the Claude command, but do not run it.
|
|
19
|
+
EOF
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
MODE="auto"
|
|
23
|
+
SOURCE=""
|
|
24
|
+
ID=""
|
|
25
|
+
while [ $# -gt 0 ]; do
|
|
26
|
+
case "$1" in
|
|
27
|
+
--print|--auto-plan)
|
|
28
|
+
MODE="print"; shift ;;
|
|
29
|
+
--manual-prompt)
|
|
30
|
+
MODE="manual"; shift ;;
|
|
31
|
+
--no-launch|--prepare-only)
|
|
32
|
+
MODE="no-launch"; shift ;;
|
|
33
|
+
-h|--help)
|
|
34
|
+
usage; exit 0 ;;
|
|
35
|
+
pr)
|
|
36
|
+
SOURCE="pr"; shift
|
|
37
|
+
if [ $# -gt 0 ] && [ -z "$ID" ]; then
|
|
38
|
+
ID="$1"; shift
|
|
39
|
+
fi
|
|
40
|
+
;;
|
|
41
|
+
-*)
|
|
42
|
+
echo "lathe plan: unknown flag $1" >&2
|
|
43
|
+
usage
|
|
44
|
+
exit 1 ;;
|
|
45
|
+
*)
|
|
46
|
+
if [ -z "$SOURCE" ]; then
|
|
47
|
+
SOURCE="pr"
|
|
48
|
+
fi
|
|
49
|
+
if [ -z "$ID" ]; then
|
|
50
|
+
ID="$1"
|
|
51
|
+
else
|
|
52
|
+
echo "lathe plan: extra argument $1" >&2
|
|
53
|
+
usage
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
shift ;;
|
|
57
|
+
esac
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
if [ "$SOURCE" != "pr" ] || [ -z "$ID" ]; then
|
|
61
|
+
usage
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
lathe_prepare_pr_task "$ID"
|
|
66
|
+
lathe_write_invocation plan
|
|
67
|
+
INITIAL_PROMPT="$(lathe_task_plan_prompt)"
|
|
68
|
+
|
|
69
|
+
cat <<EOF
|
|
70
|
+
|
|
71
|
+
Task worktree ready for planning.
|
|
72
|
+
Path: $TASK_WT
|
|
73
|
+
Local branch: $TASK_BRANCH
|
|
74
|
+
PR head branch: $HEAD_REF
|
|
75
|
+
Descriptor: $LATHE_TASK_DIR/task.json
|
|
76
|
+
Brief: $LATHE_TASK_DIR/brief.md
|
|
77
|
+
Invocation: $LATHE_TASK_DIR/invocation.json
|
|
78
|
+
Prompt: $INITIAL_PROMPT
|
|
79
|
+
EOF
|
|
80
|
+
|
|
81
|
+
case "$MODE" in
|
|
82
|
+
no-launch)
|
|
83
|
+
cat <<EOF
|
|
84
|
+
|
|
85
|
+
Run target planning when ready:
|
|
86
|
+
cd "$DEVELOP_WT"
|
|
87
|
+
claude --add-dir "$TASK_WT" "$INITIAL_PROMPT"
|
|
88
|
+
EOF
|
|
89
|
+
;;
|
|
90
|
+
|
|
91
|
+
manual)
|
|
92
|
+
cat <<EOF
|
|
93
|
+
|
|
94
|
+
Manual prompt mode. Paste this into the Claude TUI:
|
|
95
|
+
|
|
96
|
+
$INITIAL_PROMPT
|
|
97
|
+
|
|
98
|
+
Launching target for planning...
|
|
99
|
+
EOF
|
|
100
|
+
cd "$DEVELOP_WT"
|
|
101
|
+
exec claude --add-dir "$TASK_WT"
|
|
102
|
+
;;
|
|
103
|
+
|
|
104
|
+
print)
|
|
105
|
+
echo ""
|
|
106
|
+
echo "Running target planning non-interactively..."
|
|
107
|
+
cd "$DEVELOP_WT"
|
|
108
|
+
exec claude --print --permission-mode auto --add-dir "$TASK_WT" "$INITIAL_PROMPT"
|
|
109
|
+
;;
|
|
110
|
+
|
|
111
|
+
auto)
|
|
112
|
+
echo ""
|
|
113
|
+
echo "Launching target for planning with the initial prompt already submitted..."
|
|
114
|
+
cd "$DEVELOP_WT"
|
|
115
|
+
exec claude --add-dir "$TASK_WT" "$INITIAL_PROMPT"
|
|
116
|
+
;;
|
|
117
|
+
esac
|
package/bin/lathe-process
CHANGED
|
@@ -1,88 +1,170 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# lathe process <pr#> — pick up a PR for target processing.
|
|
2
|
+
# lathe process [--manual-prompt|--no-launch] <pr#> — pick up a PR for target processing.
|
|
3
3
|
# 1. fetch PR head + body via gh
|
|
4
|
-
# 2. add a worktree
|
|
5
|
-
# 3. write the
|
|
6
|
-
# 4. cd into develop worktree
|
|
4
|
+
# 2. add a task/pr-<pr#> worktree from the PR head under <project>/tasks/<pr#>
|
|
5
|
+
# 3. write the Lathe transport envelope into .lathe/task.json + .lathe/brief.md
|
|
6
|
+
# 4. cd into develop worktree, launch claude with --add-dir <task-wt>
|
|
7
|
+
# and submit the initial prompt as a CLI argument
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
# prompt
|
|
10
|
-
# is unreliable in claude's interactive mode.)
|
|
9
|
+
# If a user's Claude Code build does not accept an initial prompt argument,
|
|
10
|
+
# --manual-prompt preserves the old paste-it-yourself flow.
|
|
11
11
|
|
|
12
12
|
set -euo pipefail
|
|
13
13
|
: "${LATHE_HOME:?must be set by lathe dispatcher}"
|
|
14
14
|
source "$LATHE_HOME/bin/_lathe-lib.sh"
|
|
15
|
+
source "$LATHE_HOME/bin/_lathe-task.sh"
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
exit 1
|
|
20
|
-
fi
|
|
21
|
-
|
|
22
|
-
command -v gh >/dev/null 2>&1 || {
|
|
23
|
-
echo "lathe process: gh (GitHub CLI) is required" >&2
|
|
24
|
-
exit 1
|
|
25
|
-
}
|
|
17
|
+
usage() {
|
|
18
|
+
cat >&2 <<EOF
|
|
19
|
+
Usage: lathe process [--manual-prompt|--no-launch] <pr#>
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
echo "lathe process: develop worktree not found" >&2
|
|
33
|
-
exit 1
|
|
21
|
+
Options:
|
|
22
|
+
--manual-prompt Launch Claude without submitting the initial prompt.
|
|
23
|
+
The prompt is printed and copied to the clipboard when possible.
|
|
24
|
+
--no-launch Prepare tasks/<pr#>/ and print the Claude command, but do not run it.
|
|
25
|
+
EOF
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
PR=""
|
|
29
|
+
MODE="auto"
|
|
30
|
+
while [ $# -gt 0 ]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--manual-prompt)
|
|
33
|
+
MODE="manual"; shift ;;
|
|
34
|
+
--no-launch|--prepare-only)
|
|
35
|
+
MODE="no-launch"; shift ;;
|
|
36
|
+
-h|--help)
|
|
37
|
+
usage; exit 0 ;;
|
|
38
|
+
-*)
|
|
39
|
+
echo "lathe process: unknown flag $1" >&2
|
|
40
|
+
usage
|
|
41
|
+
exit 1 ;;
|
|
42
|
+
*)
|
|
43
|
+
if [ -z "$PR" ]; then
|
|
44
|
+
PR="$1"
|
|
45
|
+
else
|
|
46
|
+
echo "lathe process: extra argument $1" >&2
|
|
47
|
+
usage
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
shift ;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
mkdir -p "$PROJECT_ROOT/tasks"
|
|
44
|
-
TASK_WT="$PROJECT_ROOT/tasks/$PR"
|
|
45
|
-
if [ -e "$TASK_WT" ]; then
|
|
46
|
-
echo "lathe process: $TASK_WT already exists. Remove it or pick another PR." >&2
|
|
54
|
+
if [ -z "$PR" ]; then
|
|
55
|
+
usage
|
|
47
56
|
exit 1
|
|
48
57
|
fi
|
|
58
|
+
PR="${PR#\#}"
|
|
59
|
+
|
|
60
|
+
lathe_prepare_pr_task "$PR"
|
|
61
|
+
lathe_write_invocation process
|
|
62
|
+
INITIAL_PROMPT="$(lathe_task_process_prompt)"
|
|
63
|
+
|
|
64
|
+
copy_prompt_to_clipboard() {
|
|
65
|
+
if command -v pbcopy >/dev/null 2>&1; then
|
|
66
|
+
if printf '%s' "$INITIAL_PROMPT" | pbcopy 2>/dev/null; then
|
|
67
|
+
echo " Clipboard: copied via pbcopy"
|
|
68
|
+
else
|
|
69
|
+
echo " Clipboard: pbcopy failed"
|
|
70
|
+
fi
|
|
71
|
+
elif command -v wl-copy >/dev/null 2>&1; then
|
|
72
|
+
if printf '%s' "$INITIAL_PROMPT" | wl-copy 2>/dev/null; then
|
|
73
|
+
echo " Clipboard: copied via wl-copy"
|
|
74
|
+
else
|
|
75
|
+
echo " Clipboard: wl-copy failed"
|
|
76
|
+
fi
|
|
77
|
+
elif command -v xclip >/dev/null 2>&1; then
|
|
78
|
+
if printf '%s' "$INITIAL_PROMPT" | xclip -selection clipboard 2>/dev/null; then
|
|
79
|
+
echo " Clipboard: copied via xclip"
|
|
80
|
+
else
|
|
81
|
+
echo " Clipboard: xclip failed"
|
|
82
|
+
fi
|
|
83
|
+
else
|
|
84
|
+
echo " Clipboard: unavailable"
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
49
87
|
|
|
50
|
-
|
|
51
|
-
|
|
88
|
+
print_aftercare() {
|
|
89
|
+
local feature_name=""
|
|
90
|
+
case "$HEAD_REF" in
|
|
91
|
+
feature/*) feature_name="${HEAD_REF#feature/}" ;;
|
|
92
|
+
esac
|
|
52
93
|
|
|
53
|
-
|
|
54
|
-
cat > "$PROMPT_FILE" <<EOF
|
|
55
|
-
# PR #$PR: $TITLE
|
|
94
|
+
cat <<EOF
|
|
56
95
|
|
|
57
|
-
|
|
96
|
+
Target session exited.
|
|
97
|
+
|
|
98
|
+
Typical next steps:
|
|
99
|
+
gh pr view $PR --web
|
|
100
|
+
gh pr merge $PR --merge
|
|
101
|
+
cd "$DEVELOP_WT" && git pull origin develop
|
|
102
|
+
EOF
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
if [ -n "$feature_name" ]; then
|
|
105
|
+
cat <<EOF
|
|
106
|
+
cd "$PROJECT_ROOT" && lathe feature-done "$feature_name"
|
|
107
|
+
EOF
|
|
108
|
+
fi
|
|
61
109
|
|
|
62
|
-
|
|
63
|
-
1. Read the existing changes in this worktree (the vibe-coded prototype)
|
|
64
|
-
2. Plan a polished implementation (planning skill, plans/<run_id>.html)
|
|
65
|
-
3. After approval, dispatch coder/reviewer to produce a clean implementation
|
|
66
|
-
4. Coder commits and pushes to branch \`$HEAD_REF\` so the PR updates
|
|
67
|
-
5. Do NOT delete the original vibe commits — your polish goes ON TOP
|
|
110
|
+
cat <<EOF
|
|
68
111
|
|
|
69
|
-
|
|
112
|
+
When the PR is merged and no more target work is needed:
|
|
113
|
+
git --git-dir="$PROJECT_ROOT/.git" worktree remove "$TASK_WT"
|
|
114
|
+
git --git-dir="$PROJECT_ROOT/.git" branch -D "$TASK_BRANCH"
|
|
70
115
|
EOF
|
|
116
|
+
}
|
|
71
117
|
|
|
72
118
|
cat <<EOF
|
|
73
119
|
|
|
74
120
|
Task worktree ready.
|
|
75
121
|
Path: $TASK_WT
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
122
|
+
Local branch: $TASK_BRANCH
|
|
123
|
+
PR head branch: $HEAD_REF
|
|
124
|
+
Descriptor: $LATHE_TASK_DIR/task.json
|
|
125
|
+
Brief: $LATHE_TASK_DIR/brief.md
|
|
126
|
+
Invocation: $LATHE_TASK_DIR/invocation.json
|
|
127
|
+
Prompt: $INITIAL_PROMPT
|
|
128
|
+
EOF
|
|
80
129
|
|
|
81
|
-
|
|
130
|
+
case "$MODE" in
|
|
131
|
+
no-launch)
|
|
132
|
+
cat <<EOF
|
|
82
133
|
|
|
83
|
-
|
|
134
|
+
Run target when ready:
|
|
135
|
+
cd "$DEVELOP_WT"
|
|
136
|
+
claude --add-dir "$TASK_WT" "$INITIAL_PROMPT"
|
|
84
137
|
EOF
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
138
|
+
exit 0
|
|
139
|
+
;;
|
|
140
|
+
|
|
141
|
+
manual)
|
|
142
|
+
echo ""
|
|
143
|
+
echo "Manual prompt mode. Paste this into the Claude TUI:"
|
|
144
|
+
echo ""
|
|
145
|
+
echo " $INITIAL_PROMPT"
|
|
146
|
+
echo ""
|
|
147
|
+
copy_prompt_to_clipboard
|
|
148
|
+
echo ""
|
|
149
|
+
echo "Launching target..."
|
|
150
|
+
cd "$DEVELOP_WT"
|
|
151
|
+
set +e
|
|
152
|
+
claude --add-dir "$TASK_WT"
|
|
153
|
+
status=$?
|
|
154
|
+
set -e
|
|
155
|
+
print_aftercare
|
|
156
|
+
exit "$status"
|
|
157
|
+
;;
|
|
158
|
+
|
|
159
|
+
auto)
|
|
160
|
+
echo ""
|
|
161
|
+
echo "Launching target with the initial prompt already submitted..."
|
|
162
|
+
cd "$DEVELOP_WT"
|
|
163
|
+
set +e
|
|
164
|
+
claude --add-dir "$TASK_WT" "$INITIAL_PROMPT"
|
|
165
|
+
status=$?
|
|
166
|
+
set -e
|
|
167
|
+
print_aftercare
|
|
168
|
+
exit "$status"
|
|
169
|
+
;;
|
|
170
|
+
esac
|
package/bin/lathe-sync
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# lathe sync — re-sync harness/ to
|
|
2
|
+
# lathe sync — re-sync harness/ to .claude/ in the develop worktree.
|
|
3
3
|
# Useful after manual harness edits, or if post-merge hook didn't fire (e.g.,
|
|
4
4
|
# merge happened via GitHub UI).
|
|
5
5
|
|
package/bin/lathe-target
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# lathe target — cd into develop worktree
|
|
2
|
+
# lathe target — cd into the develop worktree and launch claude.
|
|
3
3
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
: "${LATHE_HOME:?must be set by lathe dispatcher}"
|
|
@@ -10,5 +10,5 @@ DEVELOP_WT="$(lathe_wt_for_branch develop)" || {
|
|
|
10
10
|
exit 1
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
cd "$DEVELOP_WT
|
|
13
|
+
cd "$DEVELOP_WT"
|
|
14
14
|
exec claude "$@"
|
package/bin/lathe-watch
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lathe watch — poll GitHub PRs targeting develop and plan new ones locally.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
: "${LATHE_HOME:?must be set by lathe dispatcher}"
|
|
6
|
+
source "$LATHE_HOME/bin/_lathe-lib.sh"
|
|
7
|
+
source "$LATHE_HOME/bin/_lathe-task.sh"
|
|
8
|
+
|
|
9
|
+
usage() {
|
|
10
|
+
cat >&2 <<EOF
|
|
11
|
+
Usage: lathe watch [--once] [--interval <seconds>] [--base <branch>] [--dry-run]
|
|
12
|
+
|
|
13
|
+
Poll open GitHub PRs targeting <branch> (default: develop). For each PR that has
|
|
14
|
+
not already been planned by this watcher, run:
|
|
15
|
+
|
|
16
|
+
lathe plan --print pr <pr#>
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--once Poll once and exit.
|
|
20
|
+
--interval <seconds> Poll interval for continuous mode. Default: 60.
|
|
21
|
+
--base <branch> PR base branch to watch. Default: develop.
|
|
22
|
+
--dry-run Print what would be planned without running Claude.
|
|
23
|
+
EOF
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ONCE=0
|
|
27
|
+
INTERVAL=60
|
|
28
|
+
BASE_BRANCH="develop"
|
|
29
|
+
DRY_RUN=0
|
|
30
|
+
while [ $# -gt 0 ]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--once)
|
|
33
|
+
ONCE=1; shift ;;
|
|
34
|
+
--interval)
|
|
35
|
+
INTERVAL="$2"; shift 2 ;;
|
|
36
|
+
--interval=*)
|
|
37
|
+
INTERVAL="${1#--interval=}"; shift ;;
|
|
38
|
+
--base)
|
|
39
|
+
BASE_BRANCH="$2"; shift 2 ;;
|
|
40
|
+
--base=*)
|
|
41
|
+
BASE_BRANCH="${1#--base=}"; shift ;;
|
|
42
|
+
--dry-run)
|
|
43
|
+
DRY_RUN=1; shift ;;
|
|
44
|
+
-h|--help)
|
|
45
|
+
usage; exit 0 ;;
|
|
46
|
+
-*)
|
|
47
|
+
echo "lathe watch: unknown flag $1" >&2
|
|
48
|
+
usage
|
|
49
|
+
exit 1 ;;
|
|
50
|
+
*)
|
|
51
|
+
echo "lathe watch: extra argument $1" >&2
|
|
52
|
+
usage
|
|
53
|
+
exit 1 ;;
|
|
54
|
+
esac
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
case "$INTERVAL" in
|
|
58
|
+
''|*[!0-9]*)
|
|
59
|
+
echo "lathe watch: --interval must be a positive integer" >&2
|
|
60
|
+
exit 1 ;;
|
|
61
|
+
esac
|
|
62
|
+
if [ "$INTERVAL" -lt 1 ]; then
|
|
63
|
+
echo "lathe watch: --interval must be >= 1" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
lathe_require_gh
|
|
68
|
+
PROJECT_ROOT="$(lathe_project_root)" || {
|
|
69
|
+
echo "lathe watch: not inside a Lathe project" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
STATE_DIR="$PROJECT_ROOT/.lathe/watch"
|
|
74
|
+
STATE_FILE="$STATE_DIR/planned-prs"
|
|
75
|
+
mkdir -p "$STATE_DIR"
|
|
76
|
+
touch "$STATE_FILE"
|
|
77
|
+
|
|
78
|
+
already_planned() {
|
|
79
|
+
grep -qxF "$1" "$STATE_FILE" 2>/dev/null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
mark_planned() {
|
|
83
|
+
if ! already_planned "$1"; then
|
|
84
|
+
printf '%s\n' "$1" >> "$STATE_FILE"
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
poll_once() {
|
|
89
|
+
local prs pr
|
|
90
|
+
prs="$(gh pr list --base "$BASE_BRANCH" --state open --json number -q '.[].number')"
|
|
91
|
+
if [ -z "$prs" ]; then
|
|
92
|
+
echo "lathe watch: no open PRs targeting $BASE_BRANCH"
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
while IFS= read -r pr; do
|
|
97
|
+
[ -n "$pr" ] || continue
|
|
98
|
+
if already_planned "$pr"; then
|
|
99
|
+
echo "lathe watch: PR #$pr already planned; skipping"
|
|
100
|
+
continue
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ "$DRY_RUN" = 1 ]; then
|
|
104
|
+
echo "lathe watch: would plan PR #$pr"
|
|
105
|
+
continue
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
echo "lathe watch: planning PR #$pr"
|
|
109
|
+
if "$LATHE_HOME/bin/lathe-plan" --print pr "$pr"; then
|
|
110
|
+
mark_planned "$pr"
|
|
111
|
+
echo "lathe watch: PR #$pr marked planned"
|
|
112
|
+
else
|
|
113
|
+
echo "lathe watch: planning PR #$pr failed; will retry on next poll" >&2
|
|
114
|
+
fi
|
|
115
|
+
done <<EOF
|
|
116
|
+
$prs
|
|
117
|
+
EOF
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
while true; do
|
|
121
|
+
poll_once
|
|
122
|
+
if [ "$ONCE" = 1 ]; then
|
|
123
|
+
exit 0
|
|
124
|
+
fi
|
|
125
|
+
sleep "$INTERVAL"
|
|
126
|
+
done
|
package/package.json
CHANGED
|
@@ -1,50 +1,80 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Sync harness/
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Sync harness/ → worktree root (gitignored runtime files).
|
|
3
|
+
# Branch-aware:
|
|
4
|
+
# develop branch → sync target part (everything in harness/ except harness/meta/)
|
|
5
|
+
# meta branch → sync meta part (harness/meta/ only)
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# Cleans stale runtime files first (rm before cp), so renames/removals on the
|
|
8
|
+
# harness side propagate.
|
|
9
|
+
#
|
|
10
|
+
# Run when:
|
|
11
|
+
# - Manually after editing harness/ (rare; usually meta does this via PR)
|
|
12
|
+
# - Automatically by the post-merge git hook on merges into develop or meta
|
|
13
|
+
# - Once at lathe init for the initial population
|
|
10
14
|
|
|
11
15
|
set -euo pipefail
|
|
12
16
|
|
|
13
17
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
|
14
18
|
HARNESS="$REPO/harness"
|
|
15
|
-
TARGET="$REPO/target"
|
|
16
19
|
|
|
17
20
|
if [ ! -d "$HARNESS" ]; then
|
|
18
|
-
echo "sync
|
|
21
|
+
echo "sync.sh: $HARNESS does not exist" >&2
|
|
19
22
|
exit 1
|
|
20
23
|
fi
|
|
21
24
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
cp -
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
cp -R "$HARNESS/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
25
|
+
# Branch detection. Detached HEAD = abort with no-op (don't mangle anything).
|
|
26
|
+
BRANCH="$(git -C "$REPO" symbolic-ref --short HEAD 2>/dev/null || true)"
|
|
27
|
+
if [ -z "$BRANCH" ]; then
|
|
28
|
+
echo "sync.sh: HEAD is detached (no branch). Skipping sync." >&2
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
case "$BRANCH" in
|
|
33
|
+
develop)
|
|
34
|
+
# ---- target side: harness/* (excluding harness/meta/) → worktree root ----
|
|
35
|
+
rm -f "$REPO/CLAUDE.md" "$REPO/plan_template.html"
|
|
36
|
+
rm -rf "$REPO/.claude" "$REPO/workflow" "$REPO/hooks"
|
|
37
|
+
|
|
38
|
+
cp -f "$HARNESS/CLAUDE.md" "$REPO/CLAUDE.md"
|
|
39
|
+
cp -f "$HARNESS/plan_template.html" "$REPO/plan_template.html"
|
|
40
|
+
|
|
41
|
+
mkdir -p "$REPO/.claude/skills" "$REPO/.claude/agents"
|
|
42
|
+
cp -f "$HARNESS/settings.json" "$REPO/.claude/settings.json"
|
|
43
|
+
cp -R "$HARNESS/skills/." "$REPO/.claude/skills/"
|
|
44
|
+
cp -R "$HARNESS/agents/." "$REPO/.claude/agents/"
|
|
45
|
+
|
|
46
|
+
mkdir -p "$REPO/workflow"
|
|
47
|
+
cp -R "$HARNESS/workflow/." "$REPO/workflow/"
|
|
48
|
+
|
|
49
|
+
mkdir -p "$REPO/hooks"
|
|
50
|
+
cp -R "$HARNESS/hooks/." "$REPO/hooks/"
|
|
51
|
+
chmod +x "$REPO/hooks"/*.sh
|
|
52
|
+
|
|
53
|
+
mkdir -p "$REPO/plans"
|
|
54
|
+
echo "sync.sh: develop runtime rebuilt from harness/"
|
|
55
|
+
;;
|
|
56
|
+
|
|
57
|
+
meta)
|
|
58
|
+
# ---- meta side: harness/meta/* → worktree root ----
|
|
59
|
+
if [ ! -d "$HARNESS/meta" ]; then
|
|
60
|
+
echo "sync.sh: $HARNESS/meta does not exist (this lathe install may be older than v1.2)" >&2
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
rm -f "$REPO/CLAUDE.md"
|
|
65
|
+
rm -rf "$REPO/.claude"
|
|
66
|
+
|
|
67
|
+
cp -f "$HARNESS/meta/CLAUDE.md" "$REPO/CLAUDE.md"
|
|
68
|
+
|
|
69
|
+
mkdir -p "$REPO/.claude/skills"
|
|
70
|
+
cp -f "$HARNESS/meta/settings.json" "$REPO/.claude/settings.json"
|
|
71
|
+
cp -R "$HARNESS/meta/skills/." "$REPO/.claude/skills/"
|
|
72
|
+
|
|
73
|
+
echo "sync.sh: meta runtime rebuilt from harness/meta/"
|
|
74
|
+
;;
|
|
75
|
+
|
|
76
|
+
*)
|
|
77
|
+
echo "sync.sh: branch '$BRANCH' is neither develop nor meta. Skipping." >&2
|
|
78
|
+
exit 0
|
|
79
|
+
;;
|
|
80
|
+
esac
|