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/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Claude Code を agent backend にした target/meta 二層 harness を、git bra
|
|
|
7
7
|
| role | やること | 接点 |
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| 開発者 | feature branch で vibe coding、PR を develop に出す | git / gh |
|
|
10
|
-
| target agent | PR を polish 実装に置き換える、commit して PR を更新 | plan HTML 出力、
|
|
10
|
+
| target agent | PR を polish 実装に置き換える、commit して PR を更新 | plan HTML 出力、workflow gate / escalation |
|
|
11
11
|
| overseer | plan HTML レビュー、PR レビュー、merge | claude UI + GitHub PR |
|
|
12
12
|
| meta operator | runs/ を読み harness/ を改善、PR を develop に出す | meta agent と対話 |
|
|
13
13
|
|
|
@@ -28,17 +28,27 @@ mkdir myapp && cd myapp
|
|
|
28
28
|
lathe init
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
これで以下ができます(v1.2 layout):
|
|
32
32
|
|
|
33
33
|
```
|
|
34
34
|
myapp/
|
|
35
35
|
├── .git/ 共有 bare repo
|
|
36
|
+
├── .claude/ project-root helper(lathe-cli skill)
|
|
37
|
+
├── CLAUDE.md helper の役割記述
|
|
36
38
|
├── main/ main worktree(あなたの app code)
|
|
37
|
-
├── develop/ develop worktree
|
|
38
|
-
|
|
39
|
+
├── develop/ develop worktree
|
|
40
|
+
│ ├── harness/ tracked、target + meta 両方の agent spec の正
|
|
41
|
+
│ │ ├── CLAUDE.md, settings.json, skills/, agents/, ... target 用
|
|
42
|
+
│ │ └── meta/ meta 用
|
|
43
|
+
│ ├── bin/sync.sh tracked(branch-aware、target or meta runtime に展開)
|
|
44
|
+
│ ├── runs/ tracked(hook auto-commit)
|
|
45
|
+
│ └── (CLAUDE.md, .claude/, hooks/, workflow/, plan_template.html — gitignored sync output)
|
|
46
|
+
└── meta/ meta worktree
|
|
47
|
+
├── harness/, bin/, runs/, improvements/ tracked(develop から inherit)
|
|
48
|
+
└── (CLAUDE.md, .claude/ — gitignored sync output、harness/meta/ から)
|
|
39
49
|
```
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
`harness/` が agent spec の唯一の正。target と meta の両方の指示書が `harness/`(target は root、meta は `harness/meta/`)に集約されているので、harness/ さえあれば同じ harness で再現可能。runtime の `.claude/` `CLAUDE.md` は gitignored で sync.sh が生成。
|
|
42
52
|
|
|
43
53
|
## 日常コマンド
|
|
44
54
|
|
|
@@ -47,9 +57,15 @@ lathe feature <name> [--from main|develop]
|
|
|
47
57
|
# features/<name>/ worktree + feature/<name> 枝
|
|
48
58
|
lathe feature-done <name> # 安全に worktree + branch + remote を一掃
|
|
49
59
|
lathe ls # worktree / branch / 最近の runs / improvements
|
|
50
|
-
lathe
|
|
51
|
-
lathe
|
|
52
|
-
lathe
|
|
60
|
+
lathe plan pr <pr#> # PR pickup → tasks/<pr#>/ worktree → target が計画だけ作る
|
|
61
|
+
lathe watch [--once] # develop 宛ての新規 PR を監視し、自動で lathe plan --print pr
|
|
62
|
+
lathe process <pr#> # PR pickup/reuse → 計画承認待ちなしで target が実装まで進める
|
|
63
|
+
lathe process --manual-prompt <pr#>
|
|
64
|
+
# 旧式 Claude Code 用。prompt を手貼りする fallback
|
|
65
|
+
lathe process --no-launch <pr#>
|
|
66
|
+
# tasks/<pr#>/ 準備だけ行い、target は起動しない
|
|
67
|
+
lathe target [args...] # cd develop && claude args
|
|
68
|
+
lathe meta [args...] # cd meta && claude args
|
|
53
69
|
lathe sync # develop/bin/sync.sh 手動実行
|
|
54
70
|
lathe help # ヘルプ
|
|
55
71
|
```
|
|
@@ -67,6 +83,17 @@ claude # Lathe 操作の質問はここで聞ける
|
|
|
67
83
|
|
|
68
84
|
helper は **Lathe 操作のアシスタント**で、target/meta のような実装エージェントではありません。`lathe feature` や PR 処理のフローに迷ったら相談する相手として常駐します。
|
|
69
85
|
|
|
86
|
+
## develop PR の自動計画
|
|
87
|
+
|
|
88
|
+
ローカルで PR を監視し、develop 宛ての新規 PR を見つけたら計画だけ自動作成できます。
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
cd myapp
|
|
92
|
+
lathe watch --interval 60
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
watch は `lathe plan --print pr <pr#>` を呼び、`tasks/<pr#>/.lathe/task.json`、`.lathe/brief.md`、`.lathe/invocation.json` を target harness に渡します。`task.json` は source / branch / worktree などの transport 事実、`invocation.json` は planning-only 制約などの runtime dependency injection です。workflow・human gate・delivery は target harness 側で選びます。
|
|
96
|
+
|
|
70
97
|
## 標準ワークフロー
|
|
71
98
|
|
|
72
99
|
```sh
|
|
@@ -80,9 +107,12 @@ git push -u origin feature/X
|
|
|
80
107
|
gh pr create --base develop --title "..." --body "..."
|
|
81
108
|
|
|
82
109
|
# overseer
|
|
110
|
+
lathe plan pr <pr#> # 任意: 計画だけ先に作る。watch なら自動
|
|
83
111
|
lathe process <pr#>
|
|
84
|
-
#
|
|
85
|
-
#
|
|
112
|
+
# tasks/<pr#>/ + task/pr-<pr#> が作られる(plan 済みなら reuse)
|
|
113
|
+
# tasks/<pr#>/.lathe/task.json + .lathe/brief.md + .lathe/invocation.json は Lathe が作る入力 envelope
|
|
114
|
+
# claude TUI が初回 prompt 入りで起動。process 実行自体が計画承認 gate になる
|
|
115
|
+
# orchestrator が plan(既存があればreuse)→ coder/reviewer dispatch → commit → PR head へ push
|
|
86
116
|
gh pr merge <pr#> # develop に merge
|
|
87
117
|
|
|
88
118
|
# meta operator(任意のタイミング)
|
|
@@ -106,9 +136,9 @@ git push origin main
|
|
|
106
136
|
## 設計の要点
|
|
107
137
|
|
|
108
138
|
- **branch / worktree でロール分離**:各 agent / role が物理的に別ディレクトリで動くので、CLAUDE.md / settings.json / hooks が混ざらない
|
|
109
|
-
- **harness は meta
|
|
139
|
+
- **harness/ は agent spec の単一の正**:target も meta も自分の設定は `harness/`(meta 用は `harness/meta/`)から sync.sh で worktree root の runtime ファイルに展開される。Claude Code 本体の `.claude/` sensitive-file guard を agent tool で突破できないため、harness/ を編集(普通の dir、guard なし)→ shell sync.sh で `.claude/` 上書き、という迂回経路
|
|
110
140
|
- **runs/ は develop で auto-commit**:target session の Stop hook が `git commit` するので、meta は `git merge develop` で読める。symlink や中央 store 不要
|
|
111
|
-
- **post-merge hook が auto-sync**:meta の harness 改善が develop に merge された瞬間に
|
|
141
|
+
- **post-merge hook が auto-sync**:meta の harness 改善が develop に merge された瞬間に develop/.claude/ が再生成される
|
|
112
142
|
|
|
113
143
|
## 動作確認済み(v1)
|
|
114
144
|
|
|
@@ -116,13 +146,14 @@ git push origin main
|
|
|
116
146
|
- `lathe init` で bare repo + 3 worktree + post-merge hook 設置
|
|
117
147
|
- target session 実行、runs auto-commit on develop
|
|
118
148
|
- meta worktree が `git merge develop` で runs/ 読込
|
|
119
|
-
- meta が harness/CLAUDE.md 編集 → develop merge → post-merge hook → sync 自動 →
|
|
149
|
+
- meta が harness/CLAUDE.md 編集 → develop merge → post-merge hook → sync 自動 → develop/CLAUDE.md 反映
|
|
120
150
|
- `lathe target` / `lathe meta` / `lathe sync` がどの worktree から実行しても正しい worktree を見つける
|
|
121
151
|
|
|
122
152
|
## 既知の v1 制限
|
|
123
153
|
|
|
124
154
|
- meta → develop merge で meta/ + improvements/ overlay も develop に持ち込まれる(cherry-pick / 別 PR 機構で v2 解決)
|
|
125
|
-
- `lathe process` の prompt
|
|
155
|
+
- `lathe process` / `lathe plan` は Claude Code の prompt 引数で初回 prompt を自動投入する。古い Claude Code で動かない場合は `--manual-prompt` を使う
|
|
156
|
+
- `lathe process` は workflow を固定せず、計画承認 grant と PR delivery adapter を `.lathe/invocation.json` で注入する。採用 workflow がそれを安全に消費できる場合は計画承認待ちを挟まず実装まで進み、仕様不明、high-risk 変更、検証不能などは human escalation として停止する
|
|
126
157
|
- post-merge hook は local merge のみ発火、GitHub UI で merge した時は `git pull` 後に `lathe sync` 手動
|
|
127
158
|
- v1 は新 project 専用、既存 repo の自動 migration なし
|
|
128
159
|
- runs/ auto-commit は local commit のみ(remote push しない)。multi-machine 構成では別途 push 設計が必要
|
|
@@ -130,4 +161,4 @@ git push origin main
|
|
|
130
161
|
## 設計詳細
|
|
131
162
|
|
|
132
163
|
target orchestrator の指示書: `template/develop/harness/CLAUDE.md`
|
|
133
|
-
meta agent の指示書: `template/
|
|
164
|
+
meta agent の指示書: `template/develop/harness/meta/CLAUDE.md`
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared task-intake helpers for Lathe subcommands. Sourced, not executed.
|
|
3
|
+
|
|
4
|
+
lathe_require_gh() {
|
|
5
|
+
command -v gh >/dev/null 2>&1 || {
|
|
6
|
+
echo "lathe: gh (GitHub CLI) is required" >&2
|
|
7
|
+
exit 1
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
lathe_write_exclude_once() {
|
|
12
|
+
local file="$1"
|
|
13
|
+
local pattern="$2"
|
|
14
|
+
touch "$file"
|
|
15
|
+
if ! grep -qxF "$pattern" "$file" 2>/dev/null; then
|
|
16
|
+
printf '%s\n' "$pattern" >> "$file"
|
|
17
|
+
fi
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Prepare a PR-backed task worktree and write Lathe's local transport envelope.
|
|
21
|
+
#
|
|
22
|
+
# Sets globals:
|
|
23
|
+
# PROJECT_ROOT DEVELOP_WT GIT_DIR PR_NUMBER PR_TITLE PR_BODY PR_URL
|
|
24
|
+
# BASE_REF HEAD_REF FETCH_REF TASK_BRANCH TASK_WT LATHE_TASK_DIR
|
|
25
|
+
lathe_prepare_pr_task() {
|
|
26
|
+
local requested_pr="${1#\#}"
|
|
27
|
+
local current_branch exclude_file
|
|
28
|
+
|
|
29
|
+
lathe_require_gh
|
|
30
|
+
|
|
31
|
+
PROJECT_ROOT="$(lathe_project_root)" || {
|
|
32
|
+
echo "lathe: not inside a Lathe project" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
DEVELOP_WT="$(lathe_wt_for_branch develop)" || {
|
|
36
|
+
echo "lathe: develop worktree not found" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
}
|
|
39
|
+
GIT_DIR="$PROJECT_ROOT/.git"
|
|
40
|
+
|
|
41
|
+
echo "==> Fetching PR #$requested_pr metadata ..."
|
|
42
|
+
PR_JSON="$(gh pr view "$requested_pr" --json number,title,body,url,baseRefName,headRefName)"
|
|
43
|
+
PR_NUMBER="$(printf '%s' "$PR_JSON" | jq -r '.number // empty')"
|
|
44
|
+
[ -n "$PR_NUMBER" ] || PR_NUMBER="$requested_pr"
|
|
45
|
+
PR_TITLE="$(printf '%s' "$PR_JSON" | jq -r '.title // ""')"
|
|
46
|
+
PR_BODY="$(printf '%s' "$PR_JSON" | jq -r '.body // ""')"
|
|
47
|
+
PR_URL="$(printf '%s' "$PR_JSON" | jq -r '.url // ""')"
|
|
48
|
+
BASE_REF="$(printf '%s' "$PR_JSON" | jq -r '.baseRefName // "develop"')"
|
|
49
|
+
HEAD_REF="$(printf '%s' "$PR_JSON" | jq -r '.headRefName // empty')"
|
|
50
|
+
if [ -z "$HEAD_REF" ]; then
|
|
51
|
+
echo "lathe: PR #$requested_pr has no headRefName" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
FETCH_REF="refs/remotes/origin/lathe/pr-$PR_NUMBER"
|
|
56
|
+
TASK_BRANCH="task/pr-$PR_NUMBER"
|
|
57
|
+
TASK_WT="$PROJECT_ROOT/tasks/$PR_NUMBER"
|
|
58
|
+
LATHE_TASK_DIR="$TASK_WT/.lathe"
|
|
59
|
+
|
|
60
|
+
if ! git --git-dir="$GIT_DIR" fetch origin "+refs/pull/$PR_NUMBER/head:$FETCH_REF" >/dev/null 2>&1; then
|
|
61
|
+
git --git-dir="$GIT_DIR" fetch origin "+$HEAD_REF:$FETCH_REF" >/dev/null 2>&1 || {
|
|
62
|
+
echo "lathe: failed to fetch PR #$PR_NUMBER from origin" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
}
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
mkdir -p "$PROJECT_ROOT/tasks"
|
|
68
|
+
if [ -e "$TASK_WT" ]; then
|
|
69
|
+
if ! git -C "$TASK_WT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
70
|
+
echo "lathe: $TASK_WT exists but is not a git worktree" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
current_branch="$(git -C "$TASK_WT" symbolic-ref --short HEAD 2>/dev/null || true)"
|
|
74
|
+
if [ "$current_branch" != "$TASK_BRANCH" ]; then
|
|
75
|
+
echo "lathe: $TASK_WT is on '$current_branch', expected '$TASK_BRANCH'" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
echo "==> Reusing task worktree at $TASK_WT (branch: $TASK_BRANCH)"
|
|
79
|
+
else
|
|
80
|
+
if git --git-dir="$GIT_DIR" rev-parse --verify "$TASK_BRANCH" >/dev/null 2>&1; then
|
|
81
|
+
echo "==> Creating task worktree at $TASK_WT (branch: $TASK_BRANCH)..."
|
|
82
|
+
git --git-dir="$GIT_DIR" worktree add --quiet "$TASK_WT" "$TASK_BRANCH"
|
|
83
|
+
else
|
|
84
|
+
echo "==> Creating task worktree at $TASK_WT (branch: $TASK_BRANCH from $HEAD_REF)..."
|
|
85
|
+
git --git-dir="$GIT_DIR" worktree add --quiet -b "$TASK_BRANCH" "$TASK_WT" "$FETCH_REF"
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
mkdir -p "$LATHE_TASK_DIR"
|
|
90
|
+
jq -n \
|
|
91
|
+
--arg source_type "pr" \
|
|
92
|
+
--arg source_id "$PR_NUMBER" \
|
|
93
|
+
--arg title "$PR_TITLE" \
|
|
94
|
+
--arg url "$PR_URL" \
|
|
95
|
+
--arg base_branch "$BASE_REF" \
|
|
96
|
+
--arg head_branch "$HEAD_REF" \
|
|
97
|
+
--arg task_branch "$TASK_BRANCH" \
|
|
98
|
+
--arg task_worktree "$TASK_WT" \
|
|
99
|
+
--arg brief ".lathe/brief.md" \
|
|
100
|
+
'{
|
|
101
|
+
schema_version: 1,
|
|
102
|
+
source: {
|
|
103
|
+
type: $source_type,
|
|
104
|
+
id: $source_id,
|
|
105
|
+
title: $title,
|
|
106
|
+
url: $url,
|
|
107
|
+
base_branch: $base_branch,
|
|
108
|
+
head_branch: $head_branch
|
|
109
|
+
},
|
|
110
|
+
worktree: {
|
|
111
|
+
path: $task_worktree,
|
|
112
|
+
branch: $task_branch
|
|
113
|
+
},
|
|
114
|
+
artifacts: {
|
|
115
|
+
brief: $brief
|
|
116
|
+
}
|
|
117
|
+
}' > "$LATHE_TASK_DIR/task.json"
|
|
118
|
+
|
|
119
|
+
cat > "$LATHE_TASK_DIR/brief.md" <<EOF
|
|
120
|
+
# PR #$PR_NUMBER: $PR_TITLE
|
|
121
|
+
|
|
122
|
+
Source: $PR_URL
|
|
123
|
+
Base branch: $BASE_REF
|
|
124
|
+
Head branch: $HEAD_REF
|
|
125
|
+
Task branch: $TASK_BRANCH
|
|
126
|
+
Task worktree: $TASK_WT
|
|
127
|
+
|
|
128
|
+
## PR body
|
|
129
|
+
|
|
130
|
+
$PR_BODY
|
|
131
|
+
EOF
|
|
132
|
+
|
|
133
|
+
exclude_file="$(git -C "$TASK_WT" rev-parse --git-path info/exclude)"
|
|
134
|
+
mkdir -p "$(dirname "$exclude_file")"
|
|
135
|
+
touch "$exclude_file"
|
|
136
|
+
if ! grep -qxF "# Lathe local task metadata" "$exclude_file" 2>/dev/null; then
|
|
137
|
+
{
|
|
138
|
+
echo ""
|
|
139
|
+
echo "# Lathe local task metadata"
|
|
140
|
+
} >> "$exclude_file"
|
|
141
|
+
fi
|
|
142
|
+
lathe_write_exclude_once "$exclude_file" ".lathe/"
|
|
143
|
+
lathe_write_exclude_once "$exclude_file" ".lathe-task.md"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
lathe_write_invocation() {
|
|
147
|
+
local mode="$1"
|
|
148
|
+
mkdir -p "$LATHE_TASK_DIR"
|
|
149
|
+
|
|
150
|
+
case "$mode" in
|
|
151
|
+
process)
|
|
152
|
+
jq -n \
|
|
153
|
+
--arg command "lathe process" \
|
|
154
|
+
--arg intent "execute_task" \
|
|
155
|
+
--arg plan_approval "granted_by_invocation" \
|
|
156
|
+
--arg delivery "update_pr_head" \
|
|
157
|
+
'{
|
|
158
|
+
schema_version: 1,
|
|
159
|
+
invocation: {
|
|
160
|
+
command: $command,
|
|
161
|
+
intent: $intent
|
|
162
|
+
},
|
|
163
|
+
injected_dependencies: {
|
|
164
|
+
plan_approval: {
|
|
165
|
+
grant: $plan_approval,
|
|
166
|
+
approver: "lathe process invocation",
|
|
167
|
+
meaning: "The overseer explicitly requested execution; do not stop solely for human plan approval if the selected workflow can consume this grant."
|
|
168
|
+
},
|
|
169
|
+
delivery: {
|
|
170
|
+
adapter: $delivery,
|
|
171
|
+
command_template: "git push origin HEAD:<source.head_branch>"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
constraints: {
|
|
175
|
+
workflow_selection: "harness_owned",
|
|
176
|
+
human_escalation: "required_when_selected_workflow_cannot_safely_consume_injected_dependencies"
|
|
177
|
+
}
|
|
178
|
+
}' > "$LATHE_TASK_DIR/invocation.json"
|
|
179
|
+
;;
|
|
180
|
+
plan)
|
|
181
|
+
jq -n \
|
|
182
|
+
--arg command "lathe plan" \
|
|
183
|
+
--arg intent "plan_only" \
|
|
184
|
+
'{
|
|
185
|
+
schema_version: 1,
|
|
186
|
+
invocation: {
|
|
187
|
+
command: $command,
|
|
188
|
+
intent: $intent
|
|
189
|
+
},
|
|
190
|
+
injected_dependencies: {},
|
|
191
|
+
constraints: {
|
|
192
|
+
workflow_selection: "harness_owned",
|
|
193
|
+
stop_at_contract_boundary: true,
|
|
194
|
+
dispatch_subagents: false,
|
|
195
|
+
edit_task_worktree: false,
|
|
196
|
+
commit_or_push: false
|
|
197
|
+
}
|
|
198
|
+
}' > "$LATHE_TASK_DIR/invocation.json"
|
|
199
|
+
;;
|
|
200
|
+
*)
|
|
201
|
+
echo "lathe: unknown invocation mode '$mode'" >&2
|
|
202
|
+
exit 1
|
|
203
|
+
;;
|
|
204
|
+
esac
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
lathe_task_process_prompt() {
|
|
208
|
+
printf 'Read .lathe/task.json, .lathe/brief.md, and .lathe/invocation.json from the added task worktree (%s), then process the task according to the target harness protocol. Treat task.json as transport facts only. Treat invocation.json as injected runtime dependencies, not as workflow selection. Select or construct the workflow in the harness. If an existing plan for this task exists, use it if it still matches task.json, brief, invocation dependencies, and the current diff; otherwise create a new plan. The process invocation injects a plan-approval grant and PR delivery adapter; consume those dependencies if the selected workflow supports them. Escalate to the human instead of implementing if the selected workflow cannot safely consume the injected dependencies.' "$TASK_WT"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
lathe_task_plan_prompt() {
|
|
212
|
+
printf 'Read .lathe/task.json, .lathe/brief.md, and .lathe/invocation.json from the added task worktree (%s), then perform planning only according to the target harness protocol. Treat task.json as transport facts only and invocation.json as injected runtime dependencies/constraints. Select or construct the workflow in the harness. Create or update plans/<run_id>.html and stop at the contract/approval boundary. Do not dispatch coder/reviewer, do not edit the task worktree, and do not commit or push.' "$TASK_WT"
|
|
213
|
+
}
|
package/bin/check-deps.sh
CHANGED
|
@@ -9,7 +9,7 @@ for cmd in git jq claude bash; do
|
|
|
9
9
|
command -v "$cmd" >/dev/null 2>&1 || missing_required+=("$cmd")
|
|
10
10
|
done
|
|
11
11
|
|
|
12
|
-
command -v gh >/dev/null 2>&1 || missing_optional+=("gh (
|
|
12
|
+
command -v gh >/dev/null 2>&1 || missing_optional+=("gh (needed for 'lathe process', 'lathe plan pr', and 'lathe watch')")
|
|
13
13
|
|
|
14
14
|
if [ "${#missing_required[@]}" -gt 0 ]; then
|
|
15
15
|
echo ""
|
package/bin/lathe
CHANGED
|
@@ -30,9 +30,9 @@ shared bare git repo, then drive target / meta agents over Claude Code.
|
|
|
30
30
|
|
|
31
31
|
Setup:
|
|
32
32
|
init Initialize Lathe in current (empty) directory.
|
|
33
|
-
Creates ./.git (bare), three worktrees,
|
|
34
|
-
hook
|
|
35
|
-
helper for Lathe
|
|
33
|
+
Creates ./.git (bare), three worktrees (main/develop/meta),
|
|
34
|
+
post-merge hook (auto-syncs harness on develop or meta merges),
|
|
35
|
+
and (interactively) a project-root .claude/ helper for Lathe ops.
|
|
36
36
|
|
|
37
37
|
Daily ops:
|
|
38
38
|
feature <name> Create features/<name>/ worktree on a new
|
|
@@ -40,13 +40,18 @@ Daily ops:
|
|
|
40
40
|
feature-done <name>
|
|
41
41
|
Tear down a feature: worktree + branch (+ remote if any).
|
|
42
42
|
Refuses if uncommitted/unpushed unless --force.
|
|
43
|
-
process <pr#>
|
|
44
|
-
|
|
43
|
+
process [--manual-prompt|--no-launch] <pr#>
|
|
44
|
+
Pick up a GitHub PR for target processing
|
|
45
|
+
(creates ./tasks/<pr#>/ worktree, launches target with prompt)
|
|
46
|
+
plan [--print|--manual-prompt|--no-launch] pr <pr#>
|
|
47
|
+
Prepare a task worktree and ask target to write a plan only
|
|
48
|
+
watch [--once] [--interval <seconds>] [--base develop]
|
|
49
|
+
Poll open PRs and run 'lathe plan --print pr <pr#>' for new ones
|
|
45
50
|
ls List worktrees, branches, recent runs/improvements
|
|
46
51
|
|
|
47
52
|
Agent launchers:
|
|
48
|
-
target [args] cd develop
|
|
49
|
-
meta [args] cd meta
|
|
53
|
+
target [args] cd develop, claude args (target's runtime sync'd from harness/)
|
|
54
|
+
meta [args] cd meta, claude args (meta's runtime sync'd from harness/meta/)
|
|
50
55
|
sync Re-run sync.sh in develop (only if post-merge missed)
|
|
51
56
|
|
|
52
57
|
Other:
|
package/bin/lathe-init
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# lathe init — initialize a Lathe project in the current directory.
|
|
3
3
|
#
|
|
4
|
-
# Layout produced (
|
|
4
|
+
# Layout produced (v1.2):
|
|
5
5
|
# ./
|
|
6
|
-
# ├── .git/
|
|
7
|
-
# ├──
|
|
8
|
-
# ├──
|
|
9
|
-
#
|
|
6
|
+
# ├── .git/ bare repo, shared by all worktrees
|
|
7
|
+
# ├── .claude/ project-root helper config (if chosen)
|
|
8
|
+
# ├── CLAUDE.md project-root helper brief (if chosen)
|
|
9
|
+
# ├── main/ worktree on 'main' branch — your app code
|
|
10
|
+
# ├── develop/ worktree on 'develop' — target agent's runtime
|
|
11
|
+
# └── meta/ worktree on 'meta' — meta agent's runtime
|
|
10
12
|
#
|
|
11
|
-
#
|
|
13
|
+
# develop tracks harness/ (incl. harness/meta/) as the canonical agent spec.
|
|
14
|
+
# bin/sync.sh on each side regenerates the gitignored runtime files at the
|
|
15
|
+
# worktree root (CLAUDE.md, .claude/, etc.) from harness/. meta inherits
|
|
16
|
+
# develop's harness/, so meta's spec lives at harness/meta/ on the develop
|
|
17
|
+
# branch (and propagates to meta via cut/merge).
|
|
12
18
|
|
|
13
19
|
set -euo pipefail
|
|
14
20
|
|
|
@@ -18,40 +24,36 @@ PROJECT_ROOT="$(pwd)"
|
|
|
18
24
|
PROJECT_NAME="$(basename "$PROJECT_ROOT")"
|
|
19
25
|
|
|
20
26
|
# --- Validation ---
|
|
27
|
+
err=0
|
|
21
28
|
if [ -e "$PROJECT_ROOT/.git" ]; then
|
|
22
|
-
echo "lathe init: $PROJECT_ROOT/.git already exists.
|
|
23
|
-
exit 1
|
|
29
|
+
echo "lathe init: $PROJECT_ROOT/.git already exists." >&2; err=1
|
|
24
30
|
fi
|
|
25
31
|
for d in main develop meta; do
|
|
26
32
|
if [ -e "$PROJECT_ROOT/$d" ]; then
|
|
27
|
-
echo "lathe init: $PROJECT_ROOT/$d already exists.
|
|
28
|
-
exit 1
|
|
33
|
+
echo "lathe init: $PROJECT_ROOT/$d already exists." >&2; err=1
|
|
29
34
|
fi
|
|
30
35
|
done
|
|
36
|
+
[ "$err" = 1 ] && exit 1
|
|
31
37
|
|
|
32
|
-
# Warn (not error) if cwd has any other content.
|
|
33
38
|
EXTRA="$(ls -A "$PROJECT_ROOT" 2>/dev/null | grep -vE '^(\.|\.\.)$' || true)"
|
|
34
39
|
if [ -n "$EXTRA" ]; then
|
|
35
|
-
echo "Note: $PROJECT_ROOT is not empty. Existing files will remain at the project root
|
|
36
|
-
echo " (Lathe v1 doesn't auto-migrate existing repos. Move files into main/ manually if needed.)"
|
|
40
|
+
echo "Note: $PROJECT_ROOT is not empty. Existing files will remain at the project root."
|
|
37
41
|
fi
|
|
38
42
|
|
|
39
43
|
GIT_DIR="$PROJECT_ROOT/.git"
|
|
40
44
|
|
|
41
|
-
echo "==> [1/
|
|
45
|
+
echo "==> [1/6] Initializing bare repo at $GIT_DIR ..."
|
|
42
46
|
git init --bare --quiet "$GIT_DIR"
|
|
43
47
|
|
|
44
|
-
echo "==> [2/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
48
|
+
echo "==> [2/6] Creating main worktree with initial commit (template/main)..."
|
|
49
|
+
# Bootstrap an initial commit so develop/meta have a base.
|
|
50
|
+
EMPTY_TREE="$(git --git-dir="$GIT_DIR" mktree </dev/null)"
|
|
51
|
+
INIT_COMMIT="$(printf 'lathe: empty initial\n' | git --git-dir="$GIT_DIR" commit-tree "$EMPTY_TREE")"
|
|
52
|
+
git --git-dir="$GIT_DIR" update-ref refs/heads/main "$INIT_COMMIT"
|
|
53
|
+
git --git-dir="$GIT_DIR" symbolic-ref HEAD refs/heads/main
|
|
54
|
+
git --git-dir="$GIT_DIR" worktree add --quiet "$PROJECT_ROOT/main" main
|
|
55
|
+
|
|
53
56
|
cp -R "$LATHE_HOME/template/main/." "$PROJECT_ROOT/main/"
|
|
54
|
-
# Replace placeholder in README
|
|
55
57
|
if [ -f "$PROJECT_ROOT/main/README.md" ]; then
|
|
56
58
|
sed -i.bak "s/{{project_name}}/$PROJECT_NAME/g" "$PROJECT_ROOT/main/README.md"
|
|
57
59
|
rm -f "$PROJECT_ROOT/main/README.md.bak"
|
|
@@ -59,31 +61,33 @@ fi
|
|
|
59
61
|
git -C "$PROJECT_ROOT/main" add -A
|
|
60
62
|
git -C "$PROJECT_ROOT/main" \
|
|
61
63
|
-c user.email="lathe@local" -c user.name="lathe" \
|
|
62
|
-
commit --quiet
|
|
64
|
+
commit --quiet -m "lathe: init main"
|
|
63
65
|
|
|
64
|
-
echo "==> [3/
|
|
66
|
+
echo "==> [3/6] Creating develop worktree from main (template/develop)..."
|
|
65
67
|
git --git-dir="$GIT_DIR" worktree add --quiet -b develop "$PROJECT_ROOT/develop" main
|
|
66
68
|
cp -R "$LATHE_HOME/template/develop/." "$PROJECT_ROOT/develop/"
|
|
69
|
+
# Initial sync writes gitignored runtime files to the develop worktree root.
|
|
67
70
|
"$PROJECT_ROOT/develop/bin/sync.sh"
|
|
68
|
-
mkdir -p "$PROJECT_ROOT/develop/runs" "$PROJECT_ROOT/develop/
|
|
69
|
-
touch "$PROJECT_ROOT/develop/runs/.gitkeep" "$PROJECT_ROOT/develop/
|
|
71
|
+
mkdir -p "$PROJECT_ROOT/develop/runs" "$PROJECT_ROOT/develop/plans"
|
|
72
|
+
touch "$PROJECT_ROOT/develop/runs/.gitkeep" "$PROJECT_ROOT/develop/plans/.gitkeep"
|
|
70
73
|
git -C "$PROJECT_ROOT/develop" add -A
|
|
71
74
|
git -C "$PROJECT_ROOT/develop" \
|
|
72
75
|
-c user.email="lathe@local" -c user.name="lathe" \
|
|
73
76
|
commit --quiet -m "lathe: target harness on develop"
|
|
74
77
|
|
|
75
|
-
echo "==> [4/
|
|
78
|
+
echo "==> [4/6] Creating meta worktree from develop..."
|
|
76
79
|
git --git-dir="$GIT_DIR" worktree add --quiet -b meta "$PROJECT_ROOT/meta" develop
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
echo "==> [5/6] Installing post-merge hook (auto-sync on develop merges)..."
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
# Run sync on the meta worktree so meta's runtime files (gitignored)
|
|
81
|
+
# get written from harness/meta/.
|
|
82
|
+
"$PROJECT_ROOT/meta/bin/sync.sh"
|
|
83
|
+
# No commits needed on meta branch here — it inherits develop's tracked
|
|
84
|
+
# content, and runtime files are gitignored.
|
|
85
|
+
|
|
86
|
+
echo "==> [5/6] Installing post-merge hook (auto-sync on develop/meta merges)..."
|
|
87
|
+
COMMON_HOOKS="$(git --git-dir="$GIT_DIR" rev-parse --git-common-dir)/hooks"
|
|
88
|
+
mkdir -p "$COMMON_HOOKS"
|
|
89
|
+
cp "$LATHE_HOME/template/git-hooks/post-merge" "$COMMON_HOOKS/post-merge"
|
|
90
|
+
chmod +x "$COMMON_HOOKS/post-merge"
|
|
87
91
|
|
|
88
92
|
echo "==> [6/6] Helper agent setup ..."
|
|
89
93
|
echo ""
|
|
@@ -123,13 +127,29 @@ if [ -n "$HELPER_KIND" ]; then
|
|
|
123
127
|
fi
|
|
124
128
|
fi
|
|
125
129
|
|
|
130
|
+
# If LATHE_HOME is nested inside the user repo (cloned as ./.lathe), remind
|
|
131
|
+
# about .gitignore. Canonicalize for macOS /tmp -> /private/tmp.
|
|
132
|
+
LATHE_HOME_CANON="$(cd "$LATHE_HOME" && pwd -P)"
|
|
133
|
+
PROJECT_ROOT_CANON="$(cd "$PROJECT_ROOT" && pwd -P)"
|
|
134
|
+
case "$LATHE_HOME_CANON" in
|
|
135
|
+
"$PROJECT_ROOT_CANON"/*)
|
|
136
|
+
REL="${LATHE_HOME_CANON#$PROJECT_ROOT_CANON/}"
|
|
137
|
+
cat <<EOF
|
|
138
|
+
|
|
139
|
+
NOTE: Lathe is installed at \`$REL/\`. To keep it out of git, add to .gitignore:
|
|
140
|
+
/$REL/
|
|
141
|
+
EOF
|
|
142
|
+
;;
|
|
143
|
+
esac
|
|
144
|
+
|
|
145
|
+
LATHE_BIN="$LATHE_HOME/bin/lathe"
|
|
126
146
|
cat <<EOF
|
|
127
147
|
|
|
128
148
|
Lathe initialized at $PROJECT_ROOT/
|
|
129
149
|
|
|
130
150
|
main/ your app code (or branch base)
|
|
131
|
-
develop/ target
|
|
132
|
-
meta/ meta agent
|
|
151
|
+
develop/ target agent runtime, runs/ auto-commit here
|
|
152
|
+
meta/ meta agent runtime
|
|
133
153
|
.git/ shared bare repo (don't touch)
|
|
134
154
|
EOF
|
|
135
155
|
if [ -n "$HELPER_KIND" ]; then
|
|
@@ -137,14 +157,15 @@ if [ -n "$HELPER_KIND" ]; then
|
|
|
137
157
|
fi
|
|
138
158
|
cat <<EOF
|
|
139
159
|
|
|
140
|
-
Next:
|
|
160
|
+
Next (replace 'lathe' with '$LATHE_BIN' or an alias):
|
|
141
161
|
cd $PROJECT_ROOT
|
|
142
162
|
EOF
|
|
143
163
|
if [ -n "$HELPER_KIND" ]; then
|
|
144
|
-
echo " claude
|
|
164
|
+
echo " claude # ask Lathe questions; lathe-cli skill loaded"
|
|
145
165
|
fi
|
|
146
166
|
cat <<EOF
|
|
147
167
|
lathe feature <name> # start a new feature in features/<name>/
|
|
168
|
+
lathe plan pr <pr#> # have target write a plan for a PR
|
|
148
169
|
lathe process <pr#> # have target process a PR
|
|
149
170
|
lathe ls # list worktrees, branches, recent runs
|
|
150
171
|
EOF
|
package/bin/lathe-meta
CHANGED