flower-trellis 0.6.0-beta.3 → 0.6.0-beta.5

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.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: trellis-flower-update
3
+ description: "手动检查和执行已安装 Flower/Trellis 强化包升级。用于用户明确要求更新或升级 flower-trellis、Flower、Trellis 强化包、skill-garden 快照、项目 flower 版本追平,或自动更新提示被稍后/跳过后仍想在对话里升级时。不要用于用户说想发版、发布版本、release、打 tag、npm publish 或修改 package 版本号;这些属于项目发版流程。"
4
+ ---
5
+
6
+ # Trellis Flower Update
7
+
8
+ 用于用户主动要求升级已安装的 Flower/Trellis 强化层时。自动 SessionStart 提示的 snooze、skip 和 cooldown 只是不主动打扰,不能阻止用户显式要求升级。
9
+
10
+ 本 skill 不是发版入口。用户说“我想发版了”、release、打 tag、npm publish、更新 package 版本号或准备发布包时,不使用本 skill;按当前项目的 release SOP、`trellis-release` 或发布规范处理。
11
+
12
+ ## Workflow
13
+
14
+ 1. 确认目标项目。用户给出路径时使用该路径,否则使用当前工作目录。
15
+ 2. 运行人工检查,默认强制刷新远端版本证据:
16
+
17
+ ```bash
18
+ flower-trellis self-check --json --manual --force-remote --target <target>
19
+ ```
20
+
21
+ 3. 解析 JSON:
22
+ - `update_available`:展示当前版本、推荐版本、release notes 摘要和 `commands.recommended`。
23
+ - `project_out_of_sync`:展示当前 Flower/Trellis 与项目记录的差异,并展示 `commands.recommended`。
24
+ - `up_to_date`:说明当前安装和项目记录已一致。
25
+ - `disabled` / `offline` / `skipped`:说明原因;不要靠重置缓存伪造可执行状态。
26
+ 4. 写入前遵守确认和安全门槛:
27
+ - 用户当前消息已经明确要求执行升级时,可以执行 `commands.recommended`。
28
+ - 用户只是询问、查看或比较版本时,只展示结果并等待确认。
29
+ - `safety.reasons` 非空时,先说明风险,再等待用户明确确认。
30
+ 5. 执行推荐命令后读取 `<flower-update-result>`。如果结果要求 `run_trellis_push_confirmation`,进入 `trellis-push`,展示文件和 commit message 后等待确认。
31
+
32
+ ## Rules
33
+
34
+ - 不直接读写 `.flower/update-check.tmp`。
35
+ - 不使用 `update-check reset`、`snooze` 或 `skip` 作为升级绕过手段。
36
+ - 不运行 `npm run release`、不打 tag、不 publish,也不修改 `package.json` 版本号。
37
+ - 不把 `self-check --manual` 用在 SessionStart 自动 hook;自动路径必须继续尊重提示节流。
38
+ - 目标项目有脏文件、活动任务或命令缺失时,按 `safety.reasons` 报告并等待用户确认。
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: trellis-worktree
3
+ description: "Prepare and diagnose Trellis usage inside linked Git worktrees. Use when the user mentions worktree, linked worktree, worktree development, missing .trellis in a worktree, or asks to make .claude/.codex/.agents/Trellis files work from a worktree."
4
+ ---
5
+
6
+ # Trellis Worktree
7
+
8
+ Use this skill before normal Trellis routing when the user's current request is about using Trellis from a linked Git worktree.
9
+
10
+ The problem is usually not task or untracked routing itself: linked worktrees often do not contain the untracked `.trellis`, `.agents`, `.codex`, or `.claude` directories that AI tools need before any Trellis hook or skill can run.
11
+
12
+ ## Workflow
13
+
14
+ 1. Identify the target worktree. Use the user's explicit path if present; otherwise use the current working directory.
15
+ 2. Locate a source worktree in the same Git worktree set that contains `.trellis/scripts/worktree_setup.py`.
16
+ 3. Run read-only diagnosis first:
17
+
18
+ ```bash
19
+ python3 <source-worktree>/.trellis/scripts/worktree_setup.py status --target <target-worktree> --json
20
+ ```
21
+
22
+ 4. If status is `needs-prepare`, run:
23
+
24
+ ```bash
25
+ python3 <source-worktree>/.trellis/scripts/worktree_setup.py prepare --target <target-worktree> --json
26
+ ```
27
+
28
+ 5. If status is `blocked` or `error`, stop and report the reason and conflict paths. Do not overwrite or delete user files.
29
+ 6. After `ready` or `prepared`, return to the user's original Trellis intent and follow the normal workflow state.
30
+
31
+ ## Source Worktree Discovery
32
+
33
+ If `<source-worktree>` is not obvious, inspect Git worktrees:
34
+
35
+ ```bash
36
+ git -C <target-worktree> worktree list --porcelain
37
+ ```
38
+
39
+ Choose a listed worktree that contains `.trellis/scripts/worktree_setup.py`. If none exists, report that this project has not been updated with `trellis-worktree` support yet.
40
+
41
+ ## Safety Rules
42
+
43
+ - The helper only projects source paths that already exist.
44
+ - MVP projection paths are `.trellis`, `.agents`, `.codex`, and `.claude`.
45
+ - Projection uses symlinks and records `<target-worktree>/.trellis-worktree.json`.
46
+ - Existing non-managed `.codex`, `.claude`, `.agents`, or `.trellis` paths are conflicts.
47
+ - Do not hand-copy platform directories. Copying creates drift between worktrees.
48
+ - Do not treat successful setup as task approval, check approval, or push approval.
49
+
50
+ ## Expected Results
51
+
52
+ - `ready`: target worktree already has valid Trellis entry points.
53
+ - `needs-prepare`: target worktree is safe to prepare; run `prepare`.
54
+ - `prepared`: symlinks and manifest were created or repaired.
55
+ - `blocked`: user-owned paths would be overwritten; stop and report `conflicts`.
56
+ - `error`: Git or source-root resolution failed; stop and report `reason`.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: trellis-flower-update
3
+ description: "手动检查和执行已安装 Flower/Trellis 强化包升级。用于用户明确要求更新或升级 flower-trellis、Flower、Trellis 强化包、skill-garden 快照、项目 flower 版本追平,或自动更新提示被稍后/跳过后仍想在对话里升级时。不要用于用户说想发版、发布版本、release、打 tag、npm publish 或修改 package 版本号;这些属于项目发版流程。"
4
+ ---
5
+
6
+ # Trellis Flower Update
7
+
8
+ 用于用户主动要求升级已安装的 Flower/Trellis 强化层时。自动 SessionStart 提示的 snooze、skip 和 cooldown 只是不主动打扰,不能阻止用户显式要求升级。
9
+
10
+ 本 skill 不是发版入口。用户说“我想发版了”、release、打 tag、npm publish、更新 package 版本号或准备发布包时,不使用本 skill;按当前项目的 release SOP、`trellis-release` 或发布规范处理。
11
+
12
+ ## Workflow
13
+
14
+ 1. 确认目标项目。用户给出路径时使用该路径,否则使用当前工作目录。
15
+ 2. 运行人工检查,默认强制刷新远端版本证据:
16
+
17
+ ```bash
18
+ flower-trellis self-check --json --manual --force-remote --target <target>
19
+ ```
20
+
21
+ 3. 解析 JSON:
22
+ - `update_available`:展示当前版本、推荐版本、release notes 摘要和 `commands.recommended`。
23
+ - `project_out_of_sync`:展示当前 Flower/Trellis 与项目记录的差异,并展示 `commands.recommended`。
24
+ - `up_to_date`:说明当前安装和项目记录已一致。
25
+ - `disabled` / `offline` / `skipped`:说明原因;不要靠重置缓存伪造可执行状态。
26
+ 4. 写入前遵守确认和安全门槛:
27
+ - 用户当前消息已经明确要求执行升级时,可以执行 `commands.recommended`。
28
+ - 用户只是询问、查看或比较版本时,只展示结果并等待确认。
29
+ - `safety.reasons` 非空时,先说明风险,再等待用户明确确认。
30
+ 5. 执行推荐命令后读取 `<flower-update-result>`。如果结果要求 `run_trellis_push_confirmation`,进入 `trellis-push`,展示文件和 commit message 后等待确认。
31
+
32
+ ## Rules
33
+
34
+ - 不直接读写 `.flower/update-check.tmp`。
35
+ - 不使用 `update-check reset`、`snooze` 或 `skip` 作为升级绕过手段。
36
+ - 不运行 `npm run release`、不打 tag、不 publish,也不修改 `package.json` 版本号。
37
+ - 不把 `self-check --manual` 用在 SessionStart 自动 hook;自动路径必须继续尊重提示节流。
38
+ - 目标项目有脏文件、活动任务或命令缺失时,按 `safety.reasons` 报告并等待用户确认。
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: trellis-worktree
3
+ description: "Prepare and diagnose Trellis usage inside linked Git worktrees. Use when the user mentions worktree, linked worktree, worktree development, missing .trellis in a worktree, or asks to make .claude/.codex/.agents/Trellis files work from a worktree."
4
+ ---
5
+
6
+ # Trellis Worktree
7
+
8
+ Use this skill before normal Trellis routing when the user's current request is about using Trellis from a linked Git worktree.
9
+
10
+ The problem is usually not task or untracked routing itself: linked worktrees often do not contain the untracked `.trellis`, `.agents`, `.codex`, or `.claude` directories that AI tools need before any Trellis hook or skill can run.
11
+
12
+ ## Workflow
13
+
14
+ 1. Identify the target worktree. Use the user's explicit path if present; otherwise use the current working directory.
15
+ 2. Locate a source worktree in the same Git worktree set that contains `.trellis/scripts/worktree_setup.py`.
16
+ 3. Run read-only diagnosis first:
17
+
18
+ ```bash
19
+ python3 <source-worktree>/.trellis/scripts/worktree_setup.py status --target <target-worktree> --json
20
+ ```
21
+
22
+ 4. If status is `needs-prepare`, run:
23
+
24
+ ```bash
25
+ python3 <source-worktree>/.trellis/scripts/worktree_setup.py prepare --target <target-worktree> --json
26
+ ```
27
+
28
+ 5. If status is `blocked` or `error`, stop and report the reason and conflict paths. Do not overwrite or delete user files.
29
+ 6. After `ready` or `prepared`, return to the user's original Trellis intent and follow the normal workflow state.
30
+
31
+ ## Source Worktree Discovery
32
+
33
+ If `<source-worktree>` is not obvious, inspect Git worktrees:
34
+
35
+ ```bash
36
+ git -C <target-worktree> worktree list --porcelain
37
+ ```
38
+
39
+ Choose a listed worktree that contains `.trellis/scripts/worktree_setup.py`. If none exists, report that this project has not been updated with `trellis-worktree` support yet.
40
+
41
+ ## Safety Rules
42
+
43
+ - The helper only projects source paths that already exist.
44
+ - MVP projection paths are `.trellis`, `.agents`, `.codex`, and `.claude`.
45
+ - Projection uses symlinks and records `<target-worktree>/.trellis-worktree.json`.
46
+ - Existing non-managed `.codex`, `.claude`, `.agents`, or `.trellis` paths are conflicts.
47
+ - Do not hand-copy platform directories. Copying creates drift between worktrees.
48
+ - Do not treat successful setup as task approval, check approval, or push approval.
49
+
50
+ ## Expected Results
51
+
52
+ - `ready`: target worktree already has valid Trellis entry points.
53
+ - `needs-prepare`: target worktree is safe to prepare; run `prepare`.
54
+ - `prepared`: symlinks and manifest were created or repaired.
55
+ - `blocked`: user-owned paths would be overwritten; stop and report `conflicts`.
56
+ - `error`: Git or source-root resolution failed; stop and report `reason`.
@@ -303,6 +303,7 @@
303
303
  "severity": "error",
304
304
  "target": ".codex/hooks/inject-workflow-state.py",
305
305
  "whenOperations": [
306
+ "workflow-state-worktree-root-fallback",
306
307
  "workflow-state-codex-session-start-guard",
307
308
  "workflow-state-stale-task-status",
308
309
  "workflow-state-untracked-helper",
@@ -312,11 +313,13 @@
312
313
  "assertion": {
313
314
  "type": "required-literal",
314
315
  "values": [
316
+ "# BEGIN skill-garden patch workflow-state-worktree-root-fallback v0.6",
315
317
  "# BEGIN skill-garden patch workflow-state-codex-session-start-guard v0.6",
316
318
  "# BEGIN skill-garden patch workflow-state-stale-task-status v0.6",
317
319
  "# BEGIN skill-garden patch workflow-state-untracked-helper v0.6",
318
320
  "# BEGIN skill-garden patch workflow-state-breadcrumb-subject v0.6",
319
321
  "# BEGIN skill-garden patch workflow-state-main-subject-routing v0.6",
322
+ "_find_trellis_root_from_git",
320
323
  "DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD = \"no-trellis\"",
321
324
  "\"ZCODE_PROJECT_DIR\": \"zcode\"",
322
325
  "Normalize `codex.dispatch_mode` from .trellis/config.yaml to \"auto\" or \"inline\"."
@@ -3,6 +3,25 @@
3
3
  "id": "inject-workflow-state-shared-runtime",
4
4
  "purpose": "hook_override",
5
5
  "operations": [
6
+ {
7
+ "id": "workflow-state-worktree-root-fallback",
8
+ "operation": "replace",
9
+ "targetPolicy": "each-existing",
10
+ "targets": [
11
+ { "kind": "file", "path": ".codex/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
12
+ { "kind": "file", "path": ".claude/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
13
+ { "kind": "file", "path": ".gemini/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
14
+ { "kind": "file", "path": ".qoder/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
15
+ { "kind": "file", "path": ".github/copilot/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
16
+ { "kind": "file", "path": ".codebuddy/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
17
+ { "kind": "file", "path": ".factory/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
18
+ { "kind": "file", "path": ".kiro/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
19
+ { "kind": "file", "path": ".trae/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" },
20
+ { "kind": "file", "path": ".zcode/hooks/inject-workflow-state.py", "missing": "skip", "markerStyle": "hash" }
21
+ ],
22
+ "selector": { "type": "literal", "source": "worktree-root-selector.py" },
23
+ "content": { "source": "worktree-root-content.py" }
24
+ },
6
25
  {
7
26
  "id": "workflow-state-codex-session-start-guard",
8
27
  "operation": "insert",
@@ -0,0 +1,70 @@
1
+ def _git_output(start: Path, *args: str) -> Optional[str]:
2
+ """Run a read-only git command for best-effort Trellis root fallback."""
3
+ import subprocess
4
+
5
+ try:
6
+ result = subprocess.run(
7
+ ["git", "-C", str(start), *args],
8
+ check=False,
9
+ stdout=subprocess.PIPE,
10
+ stderr=subprocess.DEVNULL,
11
+ text=True,
12
+ timeout=2,
13
+ )
14
+ except (OSError, subprocess.SubprocessError):
15
+ return None
16
+ if result.returncode != 0:
17
+ return None
18
+ output = result.stdout.strip()
19
+ return output or None
20
+
21
+
22
+ def _resolve_git_common_dir(start: Path) -> Optional[Path]:
23
+ """Return the current repository's common git dir."""
24
+ output = _git_output(start, "rev-parse", "--path-format=absolute", "--git-common-dir")
25
+ if output is None:
26
+ output = _git_output(start, "rev-parse", "--git-common-dir")
27
+ if output is None:
28
+ return None
29
+ common_dir = Path(output)
30
+ if common_dir.is_absolute():
31
+ return common_dir.resolve()
32
+ top_level = _git_output(start, "rev-parse", "--show-toplevel")
33
+ if top_level is None:
34
+ return None
35
+ return (Path(top_level) / common_dir).resolve()
36
+
37
+
38
+ def _find_trellis_root_from_git(start: Path) -> Optional[Path]:
39
+ """Find a sibling worktree that carries .trellis/ for this Git repository."""
40
+ common_dir = _resolve_git_common_dir(start)
41
+ if common_dir is not None:
42
+ candidate = common_dir.parent
43
+ if (candidate / ".trellis").is_dir():
44
+ return candidate
45
+
46
+ output = _git_output(start, "worktree", "list", "--porcelain")
47
+ if output is None:
48
+ return None
49
+ for line in output.splitlines():
50
+ if not line.startswith("worktree "):
51
+ continue
52
+ candidate = Path(line.split(" ", 1)[1]).expanduser().resolve()
53
+ if (candidate / ".trellis").is_dir():
54
+ return candidate
55
+ return None
56
+
57
+
58
+ def find_trellis_root(start: Path) -> Optional[Path]:
59
+ """Walk up from start, then fall back to the Git worktree carrying .trellis/.
60
+
61
+ Linked worktrees often do not contain the untracked .trellis/ runtime. The
62
+ hook still needs the main worktree's runtime state so direct-edit cursors can
63
+ be resumed from the linked worktree without copying .trellis/.
64
+ """
65
+ cur = start.resolve()
66
+ while cur != cur.parent:
67
+ if (cur / ".trellis").is_dir():
68
+ return cur
69
+ cur = cur.parent
70
+ return _find_trellis_root_from_git(start.resolve())
@@ -0,0 +1,12 @@
1
+ def find_trellis_root(start: Path) -> Optional[Path]:
2
+ """Walk up from start to find directory containing .trellis/.
3
+
4
+ Handles CWD drift: subdirectory launches, monorepo packages, etc.
5
+ Returns None if no .trellis/ found (silent no-op).
6
+ """
7
+ cur = start.resolve()
8
+ while cur != cur.parent:
9
+ if (cur / ".trellis").is_dir():
10
+ return cur
11
+ cur = cur.parent
12
+ return None
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import json
8
8
  import os
9
+ import subprocess
9
10
  import sys
10
11
  import tempfile
11
12
  import uuid
@@ -82,9 +83,9 @@ def _utc_now() -> str:
82
83
  return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
83
84
 
84
85
 
85
- def _find_repo_root(start: Path | None = None) -> Path | None:
86
- """从当前路径向上查找 Trellis 项目根目录。"""
87
- current = (start or Path.cwd()).resolve()
86
+ def _find_root_upwards(start: Path) -> Path | None:
87
+ """从指定路径向上查找带 .trellis 的目录。"""
88
+ current = start.resolve()
88
89
  if current.is_file():
89
90
  current = current.parent
90
91
  while True:
@@ -95,6 +96,80 @@ def _find_repo_root(start: Path | None = None) -> Path | None:
95
96
  current = current.parent
96
97
 
97
98
 
99
+ def _git_output(start: Path, *args: str) -> str | None:
100
+ """执行只读 git 命令并返回 stdout,失败时返回 None。"""
101
+ try:
102
+ result = subprocess.run(
103
+ ["git", "-C", str(start), *args],
104
+ check=False,
105
+ stdout=subprocess.PIPE,
106
+ stderr=subprocess.DEVNULL,
107
+ text=True,
108
+ timeout=2,
109
+ )
110
+ except (OSError, subprocess.SubprocessError):
111
+ return None
112
+ if result.returncode != 0:
113
+ return None
114
+ output = result.stdout.strip()
115
+ return output or None
116
+
117
+
118
+ def _resolve_git_common_dir(start: Path) -> Path | None:
119
+ """返回当前 Git 仓库的 common dir,兼容旧 git 的相对路径输出。"""
120
+ output = _git_output(start, "rev-parse", "--path-format=absolute", "--git-common-dir")
121
+ if output is None:
122
+ output = _git_output(start, "rev-parse", "--git-common-dir")
123
+ if output is None:
124
+ return None
125
+ common_dir = Path(output)
126
+ if common_dir.is_absolute():
127
+ return common_dir.resolve()
128
+ top_level = _git_output(start, "rev-parse", "--show-toplevel")
129
+ if top_level is None:
130
+ return None
131
+ return (Path(top_level) / common_dir).resolve()
132
+
133
+
134
+ def _find_root_from_git_worktrees(start: Path) -> Path | None:
135
+ """从同一个 Git worktree 集合里寻找承载 .trellis 的主项目根。"""
136
+ current = start.resolve()
137
+ if current.is_file():
138
+ current = current.parent
139
+
140
+ common_dir = _resolve_git_common_dir(current)
141
+ if common_dir is not None:
142
+ candidate = common_dir.parent
143
+ if (candidate / ".trellis").is_dir():
144
+ return candidate
145
+
146
+ output = _git_output(current, "worktree", "list", "--porcelain")
147
+ if output is None:
148
+ return None
149
+ for line in output.splitlines():
150
+ if not line.startswith("worktree "):
151
+ continue
152
+ candidate = Path(line.split(" ", 1)[1]).expanduser().resolve()
153
+ if (candidate / ".trellis").is_dir():
154
+ return candidate
155
+ return None
156
+
157
+
158
+ def _find_repo_root(start: Path | None = None) -> Path | None:
159
+ """查找 Trellis 项目根目录,兼容没有 .trellis 的 linked worktree。"""
160
+ current = (start or Path.cwd()).resolve()
161
+ root = _find_root_upwards(current)
162
+ if root is not None:
163
+ return root
164
+ root = _find_root_from_git_worktrees(current)
165
+ if root is not None:
166
+ return root
167
+ for parent in Path(__file__).resolve().parents:
168
+ if (parent / ".trellis").is_dir():
169
+ return parent
170
+ return None
171
+
172
+
98
173
  def _session_path(repo_root: Path, context_key: str) -> Path:
99
174
  """返回指定 session runtime 文件路径。"""
100
175
  return repo_root / ".trellis/.runtime/sessions" / f"{context_key}.json"