@caddis/cli 0.4.62 → 0.4.63
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/bundles/antigravity-plugin/guard_agy.py +2 -1
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_lanes.py +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_roadmap_run.py +7 -4
- package/bundles/antigravity-plugin/scripts/caddis_worktree_root.py +109 -0
- package/bundles/antigravity-plugin/skills/cloud-phases/SKILL.md +5 -3
- package/bundles/antigravity-plugin/skills/pr-merge/SKILL.md +5 -3
- package/bundles/antigravity-plugin/skills/roadmap/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +5 -3
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +5 -3
- package/bundles/antigravity-plugin/skills/using-git-worktrees/SKILL.md +10 -3
- package/bundles/codex/.codex/skills/devops/using-git-worktrees/SKILL.md +10 -3
- package/bundles/codex/.codex/skills/workflow/roadmap/SKILL.md +1 -1
- package/bundles/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -953,7 +953,8 @@ def shared_checkout_reason(command: str, root: str | None) -> str:
|
|
|
953
953
|
if _moves_tree(sub, args, cwd) and _declares_shared(cwd):
|
|
954
954
|
return (f"`git {sub.lower()}` moves the tree of a shared checkout (AGENTS.md says Shared checkout: yes; "
|
|
955
955
|
"a dev service or a peer uses it). Work in your own worktree instead: "
|
|
956
|
-
"`git worktree add
|
|
956
|
+
"`git worktree add <worktree root>/<repo>/<name> -b <branch> origin/<default>` (never under %TEMP%: "
|
|
957
|
+
"`caddis_worktree_root.py <name>` prints the path), and remove it when done")
|
|
957
958
|
return ""
|
|
958
959
|
|
|
959
960
|
|
|
@@ -2250,7 +2250,7 @@ def _accept_command(
|
|
|
2250
2250
|
unsafe = shared_checkout_reason(repo)
|
|
2251
2251
|
if unsafe and not getattr(args, "allow_shared", False):
|
|
2252
2252
|
print(f"refusing: the session checkout is not safe to fast-forward ({_printable(unsafe)}). "
|
|
2253
|
-
"Run the programme from a worktree of origin/<default>
|
|
2253
|
+
"Run the programme from a worktree of origin/<default> (git worktree add; `caddis_worktree_root.py` prints the path, never %TEMP%), "
|
|
2254
2254
|
"or pass --allow-shared to fast-forward here anyway")
|
|
2255
2255
|
return EXIT_USAGE
|
|
2256
2256
|
if unsafe:
|
|
@@ -30,7 +30,8 @@ with new commits on that branch (still checked out, nothing under `.caddis/roadm
|
|
|
30
30
|
persistent claim for the phase, pushes the branch, opens a PR (`gh api`) and stops `BLOCKED waiting for merge
|
|
31
31
|
<plan>#<phase> <url>` (exit 1); `--last-stop` lets the recheck task restart it once the PR merged; the restart checks
|
|
32
32
|
the merged origin roadmap against the hash taken at publish time, removes the claim, rolls that milestone up and goes
|
|
33
|
-
on. Every roadmap runs its sessions in ONE dedicated worktree,
|
|
33
|
+
on. Every roadmap runs its sessions in ONE dedicated worktree, `<worktree root>/<repo>/caddis-roadmap-<slug>-<repo tag>`
|
|
34
|
+
(`$CADDIS_WORKTREE_ROOT`, else `<parent of the main checkout>/.worktrees`, never %TEMP%; `caddis_worktree_root.py`; made with
|
|
34
35
|
`git worktree add --detach` from origin/<default> on the first session, reused on restart, recorded in the runner
|
|
35
36
|
folder, never the `--repo` checkout); when the roadmap is finished the runner removes it if it is clean, and keeps it
|
|
36
37
|
(saying so in the stop line) if not. The caps (hours, sessions, USD per session and in total) are per run, in memory. CONTINUE with the phase already merged on
|
|
@@ -111,7 +112,6 @@ import re
|
|
|
111
112
|
import socket
|
|
112
113
|
import subprocess
|
|
113
114
|
import sys
|
|
114
|
-
import tempfile
|
|
115
115
|
import time
|
|
116
116
|
import uuid
|
|
117
117
|
from collections import Counter
|
|
@@ -135,6 +135,7 @@ import caddis_redact # noqa: E402
|
|
|
135
135
|
import caddis_review # noqa: E402
|
|
136
136
|
import caddis_roadmap # noqa: E402
|
|
137
137
|
import caddis_spend # noqa: E402
|
|
138
|
+
import caddis_worktree_root # noqa: E402
|
|
138
139
|
import caddis_worktree_safety # noqa: E402
|
|
139
140
|
import caddis_workstreams # noqa: E402
|
|
140
141
|
|
|
@@ -1545,11 +1546,13 @@ def _registered_worktrees(repo: Path) -> set[Path]:
|
|
|
1545
1546
|
|
|
1546
1547
|
|
|
1547
1548
|
def worktree_path(repo: Path, slug: str, root: Path | None = None) -> Path:
|
|
1548
|
-
"""The roadmap's session worktree: the recorded path, else
|
|
1549
|
+
"""The roadmap's session worktree: the recorded path, else (a NEW one) `caddis-roadmap-<slug>-<repo tag>` under
|
|
1550
|
+
`<worktree root>/<repo>/` (`caddis_worktree_root`: `root`, `$CADDIS_WORKTREE_ROOT`, else `<parent of the main
|
|
1551
|
+
checkout>/.worktrees`; never %TEMP%)."""
|
|
1549
1552
|
recorded = _read_record(_state_file(repo, slug, "worktree")).get("path")
|
|
1550
1553
|
if isinstance(recorded, str) and recorded:
|
|
1551
1554
|
return Path(recorded)
|
|
1552
|
-
return
|
|
1555
|
+
return caddis_worktree_root.worktree_path(repo, f"{WORKTREE_PREFIX}{slug}-{_repo_tag(repo)}", root)
|
|
1553
1556
|
|
|
1554
1557
|
|
|
1555
1558
|
def _repo_tag(repo: Path) -> str:
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Where a NEW git worktree goes. One rule, one place; every caller and every doc uses it.
|
|
2
|
+
|
|
3
|
+
Never %TEMP%: Windows cleanup / Storage Sense can empty it and delete a worktree that holds unmerged work (owner
|
|
4
|
+
decision, 20 Oct 2026). The path is
|
|
5
|
+
|
|
6
|
+
<root>/<main checkout folder name>/<short-name>
|
|
7
|
+
|
|
8
|
+
where <root> is, in order:
|
|
9
|
+
1. the `root` argument (a test seam and an explicit override);
|
|
10
|
+
2. the `CADDIS_WORKTREE_ROOT` environment variable, when set and not blank;
|
|
11
|
+
3. `<parent folder of the repo's MAIN checkout>/.worktrees`. For `E:\\Projects\\<repo>` that is
|
|
12
|
+
`E:\\Projects\\.worktrees\\<repo>\\<short-name>`. No machine path is written in code. There is no %TEMP% fallback.
|
|
13
|
+
|
|
14
|
+
The main checkout is the parent of `git rev-parse --git-common-dir`, so the answer is the same from the main checkout,
|
|
15
|
+
from a subfolder and from any linked worktree.
|
|
16
|
+
|
|
17
|
+
Only the default for a NEW worktree lives here. A worktree that already exists, or whose path a runner recorded,
|
|
18
|
+
stays where it is: moving it breaks a venv, which keeps absolute paths.
|
|
19
|
+
|
|
20
|
+
worktree_root(repo, root=None) -> the folder that holds the per-repo folders
|
|
21
|
+
worktree_path(repo, short_name, root=None) -> the path for a new worktree (nothing is created)
|
|
22
|
+
main_checkout(repo) -> the main checkout's folder
|
|
23
|
+
|
|
24
|
+
CLI: `python caddis_worktree_root.py [--repo PATH] [--root PATH] <short-name>` prints the path. Exit 2 on a bad name
|
|
25
|
+
or a folder that is not in a git repo.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import argparse
|
|
31
|
+
import os
|
|
32
|
+
import subprocess
|
|
33
|
+
import sys
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
ENV_VAR = "CADDIS_WORKTREE_ROOT"
|
|
37
|
+
FALLBACK_DIR = ".worktrees"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class NotARepoError(RuntimeError):
|
|
41
|
+
"""The folder is not inside a git repository."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _git_out(cwd: Path, *args: str) -> str | None:
|
|
45
|
+
try:
|
|
46
|
+
res = subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True, encoding="utf-8",
|
|
47
|
+
errors="replace", stdin=subprocess.DEVNULL, check=False)
|
|
48
|
+
except OSError:
|
|
49
|
+
return None
|
|
50
|
+
return res.stdout.strip() if res.returncode == 0 else None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main_checkout(repo: Path | str) -> Path:
|
|
54
|
+
"""The MAIN checkout's folder for `repo` (the main checkout, a subfolder of it or a linked worktree).
|
|
55
|
+
|
|
56
|
+
It is the parent of the git common dir. A common dir not named `.git` (a submodule's, a bare repo's) has no such
|
|
57
|
+
parent: then the work-tree top is used, or the common dir itself when there is no work tree."""
|
|
58
|
+
repo = Path(repo).absolute()
|
|
59
|
+
common = _git_out(repo, "rev-parse", "--git-common-dir") if repo.is_dir() else None
|
|
60
|
+
if not common:
|
|
61
|
+
raise NotARepoError(f"{repo} is not inside a git repository")
|
|
62
|
+
common_dir = Path(os.path.normpath(repo / common)) # relative to `repo`, or absolute already
|
|
63
|
+
if common_dir.name == ".git":
|
|
64
|
+
return common_dir.parent
|
|
65
|
+
top = _git_out(repo, "rev-parse", "--show-toplevel")
|
|
66
|
+
return Path(os.path.normpath(top)) if top else common_dir
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _explicit_root(root: Path | str | None) -> Path | None:
|
|
70
|
+
"""`root`, else `$CADDIS_WORKTREE_ROOT` when not blank, else None (the fallback needs the main checkout)."""
|
|
71
|
+
if root:
|
|
72
|
+
return Path(root)
|
|
73
|
+
env = os.environ.get(ENV_VAR, "").strip()
|
|
74
|
+
return Path(env) if env else None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def worktree_root(repo: Path | str, root: Path | str | None = None) -> Path:
|
|
78
|
+
"""The folder that holds one folder per repo: `root`, else `$CADDIS_WORKTREE_ROOT`, else
|
|
79
|
+
`<parent of the main checkout>/.worktrees`."""
|
|
80
|
+
return _explicit_root(root) or main_checkout(repo).parent / FALLBACK_DIR
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def worktree_path(repo: Path | str, short_name: str, root: Path | str | None = None) -> Path:
|
|
84
|
+
"""The path for a NEW worktree of `repo`: `<root>/<main checkout folder name>/<short_name>`. Creates nothing.
|
|
85
|
+
`short_name` is one plain folder name: ValueError for an empty name, `.`, `..`, or one with a path separator or a
|
|
86
|
+
colon (a drive such as `C:foo` would replace the root on Windows)."""
|
|
87
|
+
if (not short_name or short_name in (".", "..") or any(c in short_name for c in "/\:")
|
|
88
|
+
or short_name != short_name.strip()):
|
|
89
|
+
raise ValueError(f"the short name must be one plain folder name, not {short_name!r}")
|
|
90
|
+
main = main_checkout(repo) # one git call
|
|
91
|
+
return (_explicit_root(root) or main.parent / FALLBACK_DIR) / main.name / short_name
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main(argv: list[str] | None = None) -> int:
|
|
95
|
+
ap = argparse.ArgumentParser(description="Print the path for a new git worktree (never under %TEMP%).")
|
|
96
|
+
ap.add_argument("short_name", help="one folder name, e.g. fix-login")
|
|
97
|
+
ap.add_argument("--repo", default=".", help="any folder of the repo or of one of its worktrees (default: .)")
|
|
98
|
+
ap.add_argument("--root", default=None, help=f"override the root (default: ${ENV_VAR}, then <repo parent>/.worktrees)")
|
|
99
|
+
args = ap.parse_args(argv)
|
|
100
|
+
try:
|
|
101
|
+
print(worktree_path(args.repo, args.short_name, args.root))
|
|
102
|
+
except (ValueError, NotARepoError) as exc:
|
|
103
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
104
|
+
return 2
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
sys.exit(main())
|
|
@@ -37,10 +37,10 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
37
37
|
|
|
38
38
|
- **On `SHARED: no`,** do the steps as written, in this checkout.
|
|
39
39
|
- **On `SHARED: yes`,** never move this checkout. Leave its changes as they are, and report them. Instead:
|
|
40
|
-
- **Rebase:** `git worktree add --detach "$
|
|
40
|
+
- **Rebase:** `git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" rebase-<name>)" <branch>`, then
|
|
41
41
|
`git -C <worktree> rebase origin/<default>` and `git -C <worktree> push --force-with-lease origin HEAD:<branch>`.
|
|
42
42
|
- **Pull a branch to test it:** `git fetch origin`, then
|
|
43
|
-
`git worktree add --detach "$
|
|
43
|
+
`git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" test-<name>)" origin/<branch>` (for the default branch,
|
|
44
44
|
`origin/<default>`), and run the gates there. Install dependencies fresh; never link `node_modules` or a venv.
|
|
45
45
|
- **Clean up after a merge:** do not switch or pull. Run `git fetch --prune`, then
|
|
46
46
|
`git fetch origin <default>:<default>`: it fast-forwards the default branch with no checkout. Git refuses when a
|
|
@@ -48,7 +48,9 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
48
48
|
checkout has it out.
|
|
49
49
|
- Remove each worktree you made when its step ends: `git worktree remove <worktree>`.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
`<name>` is the branch name with each `/` written as `-`. `caddis_worktree_root.py` prints the path of a new worktree:
|
|
52
|
+
`$CADDIS_WORKTREE_ROOT/<repo>/<short-name>`, else `<parent of the main checkout>/.worktrees/<repo>/<short-name>`. Never `%TEMP%`: Windows
|
|
53
|
+
cleanup can empty it. The caddis guard checks only the `Shared checkout: yes` line in
|
|
52
54
|
`AGENTS.md`, not a dirty tree: with that line, it asks before a tree move, and denies it in a headless session.
|
|
53
55
|
<!-- /checkout-safety -->
|
|
54
56
|
|
|
@@ -43,10 +43,10 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
43
43
|
|
|
44
44
|
- **On `SHARED: no`,** do the steps as written, in this checkout.
|
|
45
45
|
- **On `SHARED: yes`,** never move this checkout. Leave its changes as they are, and report them. Instead:
|
|
46
|
-
- **Rebase:** `git worktree add --detach "$
|
|
46
|
+
- **Rebase:** `git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" rebase-<name>)" <branch>`, then
|
|
47
47
|
`git -C <worktree> rebase origin/<default>` and `git -C <worktree> push --force-with-lease origin HEAD:<branch>`.
|
|
48
48
|
- **Pull a branch to test it:** `git fetch origin`, then
|
|
49
|
-
`git worktree add --detach "$
|
|
49
|
+
`git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" test-<name>)" origin/<branch>` (for the default branch,
|
|
50
50
|
`origin/<default>`), and run the gates there. Install dependencies fresh; never link `node_modules` or a venv.
|
|
51
51
|
- **Clean up after a merge:** do not switch or pull. Run `git fetch --prune`, then
|
|
52
52
|
`git fetch origin <default>:<default>`: it fast-forwards the default branch with no checkout. Git refuses when a
|
|
@@ -54,7 +54,9 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
54
54
|
checkout has it out.
|
|
55
55
|
- Remove each worktree you made when its step ends: `git worktree remove <worktree>`.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
`<name>` is the branch name with each `/` written as `-`. `caddis_worktree_root.py` prints the path of a new worktree:
|
|
58
|
+
`$CADDIS_WORKTREE_ROOT/<repo>/<short-name>`, else `<parent of the main checkout>/.worktrees/<repo>/<short-name>`. Never `%TEMP%`: Windows
|
|
59
|
+
cleanup can empty it. The caddis guard checks only the `Shared checkout: yes` line in
|
|
58
60
|
`AGENTS.md`, not a dirty tree: with that line, it asks before a tree move, and denies it in a headless session.
|
|
59
61
|
<!-- /checkout-safety -->
|
|
60
62
|
|
|
@@ -154,7 +154,7 @@ unattended; `-Plan <slug>` runs one plain plan instead (the two are exclusive).
|
|
|
154
154
|
or the owner, or that has **no Lane line**, is not run (fail closed): the runner adds
|
|
155
155
|
`block --type owner-gate --reason "phase <n> lane: <lane>"` (`lane: none` when there is no line) and goes on.
|
|
156
156
|
3. A `todo` milestone is set `in-progress` (a docs PR). Every session of a roadmap runs in ONE dedicated worktree,
|
|
157
|
-
|
|
157
|
+
`<worktree root>/<repo>/caddis-roadmap-<slug>-<8 hex of the repo's git common dir>` (`$CADDIS_WORKTREE_ROOT`, else the `.worktrees` folder beside the main checkout, never `%TEMP%`; an existing recorded worktree stays where it is): made with `git worktree add --detach` from `origin/<default>` on the first
|
|
158
158
|
session, reused on restart, recorded in the runner folder, never the `-Repo` checkout (plan mode keeps working in
|
|
159
159
|
the given checkout). In that worktree the runner makes a fresh branch `caddis/roadmap/<slug>/<plan>-p<n>` from
|
|
160
160
|
`origin/<default>` (it refuses a branch of that name already on origin that it did not make) and ONE session runs
|
|
@@ -42,10 +42,10 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
42
42
|
|
|
43
43
|
- **On `SHARED: no`,** do the steps as written, in this checkout.
|
|
44
44
|
- **On `SHARED: yes`,** never move this checkout. Leave its changes as they are, and report them. Instead:
|
|
45
|
-
- **Rebase:** `git worktree add --detach "$
|
|
45
|
+
- **Rebase:** `git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" rebase-<name>)" <branch>`, then
|
|
46
46
|
`git -C <worktree> rebase origin/<default>` and `git -C <worktree> push --force-with-lease origin HEAD:<branch>`.
|
|
47
47
|
- **Pull a branch to test it:** `git fetch origin`, then
|
|
48
|
-
`git worktree add --detach "$
|
|
48
|
+
`git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" test-<name>)" origin/<branch>` (for the default branch,
|
|
49
49
|
`origin/<default>`), and run the gates there. Install dependencies fresh; never link `node_modules` or a venv.
|
|
50
50
|
- **Clean up after a merge:** do not switch or pull. Run `git fetch --prune`, then
|
|
51
51
|
`git fetch origin <default>:<default>`: it fast-forwards the default branch with no checkout. Git refuses when a
|
|
@@ -53,7 +53,9 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
53
53
|
checkout has it out.
|
|
54
54
|
- Remove each worktree you made when its step ends: `git worktree remove <worktree>`.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
`<name>` is the branch name with each `/` written as `-`. `caddis_worktree_root.py` prints the path of a new worktree:
|
|
57
|
+
`$CADDIS_WORKTREE_ROOT/<repo>/<short-name>`, else `<parent of the main checkout>/.worktrees/<repo>/<short-name>`. Never `%TEMP%`: Windows
|
|
58
|
+
cleanup can empty it. The caddis guard checks only the `Shared checkout: yes` line in
|
|
57
59
|
`AGENTS.md`, not a dirty tree: with that line, it asks before a tree move, and denies it in a headless session.
|
|
58
60
|
<!-- /checkout-safety -->
|
|
59
61
|
|
|
@@ -41,10 +41,10 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
41
41
|
|
|
42
42
|
- **On `SHARED: no`,** do the steps as written, in this checkout.
|
|
43
43
|
- **On `SHARED: yes`,** never move this checkout. Leave its changes as they are, and report them. Instead:
|
|
44
|
-
- **Rebase:** `git worktree add --detach "$
|
|
44
|
+
- **Rebase:** `git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" rebase-<name>)" <branch>`, then
|
|
45
45
|
`git -C <worktree> rebase origin/<default>` and `git -C <worktree> push --force-with-lease origin HEAD:<branch>`.
|
|
46
46
|
- **Pull a branch to test it:** `git fetch origin`, then
|
|
47
|
-
`git worktree add --detach "$
|
|
47
|
+
`git worktree add --detach "$(python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_worktree_root.py" test-<name>)" origin/<branch>` (for the default branch,
|
|
48
48
|
`origin/<default>`), and run the gates there. Install dependencies fresh; never link `node_modules` or a venv.
|
|
49
49
|
- **Clean up after a merge:** do not switch or pull. Run `git fetch --prune`, then
|
|
50
50
|
`git fetch origin <default>:<default>`: it fast-forwards the default branch with no checkout. Git refuses when a
|
|
@@ -52,7 +52,9 @@ It prints `SHARED: yes <reason>` or `SHARED: no` and exits 0. It says yes when t
|
|
|
52
52
|
checkout has it out.
|
|
53
53
|
- Remove each worktree you made when its step ends: `git worktree remove <worktree>`.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
`<name>` is the branch name with each `/` written as `-`. `caddis_worktree_root.py` prints the path of a new worktree:
|
|
56
|
+
`$CADDIS_WORKTREE_ROOT/<repo>/<short-name>`, else `<parent of the main checkout>/.worktrees/<repo>/<short-name>`. Never `%TEMP%`: Windows
|
|
57
|
+
cleanup can empty it. The caddis guard checks only the `Shared checkout: yes` line in
|
|
56
58
|
`AGENTS.md`, not a dirty tree: with that line, it asks before a tree move, and denies it in a headless session.
|
|
57
59
|
<!-- /checkout-safety -->
|
|
58
60
|
|
|
@@ -31,13 +31,20 @@ If none is true, stop here and use `git switch -c`.
|
|
|
31
31
|
1. Fetch: `git fetch origin`.
|
|
32
32
|
2. Find the default branch (`main` in most repos): `git symbolic-ref --short refs/remotes/origin/HEAD`.
|
|
33
33
|
It prints `origin/main`. Use the part after `origin/` as `<default>`.
|
|
34
|
-
3. Create the worktree from `origin/<default>`, never from the local HEAD,
|
|
34
|
+
3. Create the worktree from `origin/<default>`, never from the local HEAD, at
|
|
35
|
+
`<worktree root>\<repo>\<short-name>`. Never under `%TEMP%`: Windows cleanup (Storage Sense) can empty it
|
|
36
|
+
and delete a worktree that holds unmerged work. The worktree root is `$env:CADDIS_WORKTREE_ROOT` when set,
|
|
37
|
+
else the `.worktrees` folder beside the main checkout (for `E:\Projects\<repo>` that is
|
|
38
|
+
`E:\Projects\.worktrees\<repo>\<short-name>`). `<repo>` is the main checkout's folder name. The caddis
|
|
39
|
+
script `caddis_worktree_root.py` prints the path; use it so the rule lives in one place:
|
|
35
40
|
|
|
36
41
|
```powershell
|
|
37
|
-
|
|
42
|
+
$wt = python "<caddis scripts>\caddis_worktree_root.py" <short-name> # run in the repo; prints the path
|
|
43
|
+
git worktree add -b <branch> $wt origin/<default>
|
|
38
44
|
```
|
|
39
45
|
|
|
40
|
-
Do not create worktrees inside the
|
|
46
|
+
`<caddis scripts>` is the `scripts` folder of the installed caddis plugin. Do not create worktrees inside the
|
|
47
|
+
repo. An existing worktree stays where it is: moving it breaks a venv, which keeps absolute paths.
|
|
41
48
|
4. State the full path of the worktree. Work only there, and do not switch branches in it.
|
|
42
49
|
|
|
43
50
|
## Install dependencies fresh
|
|
@@ -31,13 +31,20 @@ If none is true, stop here and use `git switch -c`.
|
|
|
31
31
|
1. Fetch: `git fetch origin`.
|
|
32
32
|
2. Find the default branch (`main` in most repos): `git symbolic-ref --short refs/remotes/origin/HEAD`.
|
|
33
33
|
It prints `origin/main`. Use the part after `origin/` as `<default>`.
|
|
34
|
-
3. Create the worktree from `origin/<default>`, never from the local HEAD,
|
|
34
|
+
3. Create the worktree from `origin/<default>`, never from the local HEAD, at
|
|
35
|
+
`<worktree root>\<repo>\<short-name>`. Never under `%TEMP%`: Windows cleanup (Storage Sense) can empty it
|
|
36
|
+
and delete a worktree that holds unmerged work. The worktree root is `$env:CADDIS_WORKTREE_ROOT` when set,
|
|
37
|
+
else the `.worktrees` folder beside the main checkout (for `E:\Projects\<repo>` that is
|
|
38
|
+
`E:\Projects\.worktrees\<repo>\<short-name>`). `<repo>` is the main checkout's folder name. The caddis
|
|
39
|
+
script `caddis_worktree_root.py` prints the path; use it so the rule lives in one place:
|
|
35
40
|
|
|
36
41
|
```powershell
|
|
37
|
-
|
|
42
|
+
$wt = python "<caddis scripts>\caddis_worktree_root.py" <short-name> # run in the repo; prints the path
|
|
43
|
+
git worktree add -b <branch> $wt origin/<default>
|
|
38
44
|
```
|
|
39
45
|
|
|
40
|
-
Do not create worktrees inside the
|
|
46
|
+
`<caddis scripts>` is the `scripts` folder of the installed caddis plugin. Do not create worktrees inside the
|
|
47
|
+
repo. An existing worktree stays where it is: moving it breaks a venv, which keeps absolute paths.
|
|
41
48
|
4. State the full path of the worktree. Work only there, and do not switch branches in it.
|
|
42
49
|
|
|
43
50
|
## Install dependencies fresh
|
|
@@ -154,7 +154,7 @@ unattended; `-Plan <slug>` runs one plain plan instead (the two are exclusive).
|
|
|
154
154
|
or the owner, or that has **no Lane line**, is not run (fail closed): the runner adds
|
|
155
155
|
`block --type owner-gate --reason "phase <n> lane: <lane>"` (`lane: none` when there is no line) and goes on.
|
|
156
156
|
3. A `todo` milestone is set `in-progress` (a docs PR). Every session of a roadmap runs in ONE dedicated worktree,
|
|
157
|
-
|
|
157
|
+
`<worktree root>/<repo>/caddis-roadmap-<slug>-<8 hex of the repo's git common dir>` (`$CADDIS_WORKTREE_ROOT`, else the `.worktrees` folder beside the main checkout, never `%TEMP%`; an existing recorded worktree stays where it is): made with `git worktree add --detach` from `origin/<default>` on the first
|
|
158
158
|
session, reused on restart, recorded in the runner folder, never the `-Repo` checkout (plan mode keeps working in
|
|
159
159
|
the given checkout). In that worktree the runner makes a fresh branch `caddis/roadmap/<slug>/<plan>-p<n>` from
|
|
160
160
|
`origin/<default>` (it refuses a branch of that name already on origin that it did not make) and ONE session runs
|
package/bundles/manifest.json
CHANGED
package/package.json
CHANGED