worktree-env 0.3.0__tar.gz → 0.3.1__tar.gz

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.
Files changed (32) hide show
  1. {worktree_env-0.3.0 → worktree_env-0.3.1}/CHANGELOG.md +5 -0
  2. {worktree_env-0.3.0 → worktree_env-0.3.1}/PKG-INFO +7 -7
  3. {worktree_env-0.3.0 → worktree_env-0.3.1}/README.md +6 -6
  4. {worktree_env-0.3.0 → worktree_env-0.3.1}/README.zh-CN.md +2 -2
  5. {worktree_env-0.3.0 → worktree_env-0.3.1}/pyproject.toml +1 -1
  6. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/__init__.py +1 -1
  7. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/config.py +3 -3
  8. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/reconciler.py +4 -1
  9. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_config.py +1 -1
  10. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_reconciler.py +24 -0
  11. {worktree_env-0.3.0 → worktree_env-0.3.1}/uv.lock +1 -1
  12. {worktree_env-0.3.0 → worktree_env-0.3.1}/.github/workflows/publish.yml +0 -0
  13. {worktree_env-0.3.0 → worktree_env-0.3.1}/.github/workflows/tests.yml +0 -0
  14. {worktree_env-0.3.0 → worktree_env-0.3.1}/.gitignore +0 -0
  15. {worktree_env-0.3.0 → worktree_env-0.3.1}/CONTRIBUTING.md +0 -0
  16. {worktree_env-0.3.0 → worktree_env-0.3.1}/LICENSE +0 -0
  17. {worktree_env-0.3.0 → worktree_env-0.3.1}/SECURITY.md +0 -0
  18. {worktree_env-0.3.0 → worktree_env-0.3.1}/examples/fullstack.yaml +0 -0
  19. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/__main__.py +0 -0
  20. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/cli.py +0 -0
  21. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/hooks.py +0 -0
  22. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/paths.py +0 -0
  23. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/profiles.py +0 -0
  24. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/projector.py +0 -0
  25. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/registry.py +0 -0
  26. {worktree_env-0.3.0 → worktree_env-0.3.1}/src/worktree_env/utils.py +0 -0
  27. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/conftest.py +0 -0
  28. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_cli.py +0 -0
  29. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_hooks.py +0 -0
  30. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_profiles.py +0 -0
  31. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_projector.py +0 -0
  32. {worktree_env-0.3.0 → worktree_env-0.3.1}/tests/test_registry.py +0 -0
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.3.1] - 2026-08-21
8
+
9
+ - Run profile initializers from the Monitor Reconciler so worktrees created by
10
+ sandboxed coding agents and IDEs receive the same setup as hook-created worktrees.
11
+
7
12
  ## [0.3.0] - 2026-08-21
8
13
 
9
14
  - Changed profile initializers to `command` and `args` string arrays, executed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: worktree-env
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Per-worktree ports, secrets, and local environment setup for Git
5
5
  Project-URL: Homepage, https://github.com/archcst/worktree-env
6
6
  Project-URL: Repository, https://github.com/archcst/worktree-env
@@ -262,8 +262,8 @@ minimal.
262
262
  The Reconciler:
263
263
 
264
264
  1. Runs `git worktree list --porcelain` to retrieve the actual list of worktrees.
265
- 2. Compares it with `ports.json`, then assigns ports, mounts secrets, and generates
266
- files for unregistered worktrees.
265
+ 2. Compares it with `ports.json`, then assigns ports, mounts secrets, generates
266
+ files, and starts configured initializers for unregistered worktrees.
267
267
 
268
268
  After adding a profile or changing a `main_worktree` path, run this command again:
269
269
 
@@ -306,10 +306,10 @@ Create a worktree
306
306
  → By the time the user or AI starts the project, dependencies are usually ready
307
307
  ```
308
308
 
309
- Asynchronous initialization is started only by the normal `post-checkout` hook.
310
- Neither `wte sync` nor the Monitor Reconciler runs these commands, preventing manual
311
- synchronization or background reconciliation from repeatedly starting expensive
312
- tasks.
309
+ Asynchronous initialization is started by the normal `post-checkout` hook or by the
310
+ Monitor Reconciler when it discovers an unregistered worktree. The Reconciler does
311
+ not process registered worktrees again, and each command's `skip_if` marker provides
312
+ additional protection against repeated setup. `wte sync` does not run initializers.
313
313
 
314
314
  ## Commands supported by `wte`
315
315
 
@@ -235,8 +235,8 @@ minimal.
235
235
  The Reconciler:
236
236
 
237
237
  1. Runs `git worktree list --porcelain` to retrieve the actual list of worktrees.
238
- 2. Compares it with `ports.json`, then assigns ports, mounts secrets, and generates
239
- files for unregistered worktrees.
238
+ 2. Compares it with `ports.json`, then assigns ports, mounts secrets, generates
239
+ files, and starts configured initializers for unregistered worktrees.
240
240
 
241
241
  After adding a profile or changing a `main_worktree` path, run this command again:
242
242
 
@@ -279,10 +279,10 @@ Create a worktree
279
279
  → By the time the user or AI starts the project, dependencies are usually ready
280
280
  ```
281
281
 
282
- Asynchronous initialization is started only by the normal `post-checkout` hook.
283
- Neither `wte sync` nor the Monitor Reconciler runs these commands, preventing manual
284
- synchronization or background reconciliation from repeatedly starting expensive
285
- tasks.
282
+ Asynchronous initialization is started by the normal `post-checkout` hook or by the
283
+ Monitor Reconciler when it discovers an unregistered worktree. The Reconciler does
284
+ not process registered worktrees again, and each command's `skip_if` marker provides
285
+ additional protection against repeated setup. `wte sync` does not run initializers.
286
286
 
287
287
  ## Commands supported by `wte`
288
288
 
@@ -188,7 +188,7 @@ wte monitor enable
188
188
  Reconciler 会:
189
189
 
190
190
  1. 执行 `git worktree list --porcelain` 获取真实 Worktree 列表。
191
- 2. 与 `ports.json` 对比,为尚未注册的 Worktree 分配端口、挂载 Secrets、生成文件。
191
+ 2. 与 `ports.json` 对比,为尚未注册的 Worktree 分配端口、挂载 Secrets、生成文件并启动配置的初始化命令。
192
192
 
193
193
  新增 Profile 或修改 `main_worktree` 路径后,需要再次执行:
194
194
 
@@ -230,7 +230,7 @@ init:
230
230
  → 用户或 AI 启动项目时,依赖通常已经准备完成
231
231
  ```
232
232
 
233
- 异步初始化只由正常的 `post-checkout` Hook 启动。`wte sync` 和 Monitor Reconciler 都不会执行这些命令,避免手动同步或后台补偿重复启动耗时任务。
233
+ 异步初始化由正常的 `post-checkout` Hook 启动;当 Monitor Reconciler 发现尚未注册的 Worktree 时也会启动。Reconciler 不会重复处理已注册的 Worktree,同时每条命令的 `skip_if` 标记可进一步避免重复初始化。`wte sync` 不会执行初始化命令。
234
234
 
235
235
  ## `wte` 支持的命令
236
236
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "worktree-env"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Per-worktree ports, secrets, and local environment setup for Git"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """worktree-env public package."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.1"
@@ -53,9 +53,9 @@ writes:
53
53
  PORT=${backend}
54
54
  CORS_ORIGIN=http://127.0.0.1:${frontend}
55
55
 
56
- # Optional post-checkout initializers. Commands are trusted local configuration
57
- # executed directly in the background. args are appended to command, and skip_if
58
- # is resolved relative to cwd.
56
+ # Optional worktree initializers started by post-checkout or the Monitor Reconciler.
57
+ # Commands are trusted local configuration executed directly in the background.
58
+ # args are appended to command, and skip_if is resolved relative to cwd.
59
59
  init:
60
60
  - command: [npm]
61
61
  args: [install]
@@ -110,7 +110,10 @@ def reconcile_once(paths: AppPaths) -> ReconcileResult:
110
110
  # Git may publish common-dir metadata before checkout is complete.
111
111
  if worktree_root(candidate) != candidate:
112
112
  raise WteError(f"worktree root is not stable yet: {candidate}")
113
- result = apply_worktree(paths, candidate, setup=False)
113
+ # The reconciler is the fallback for sandboxed Git clients that
114
+ # cannot invoke the host's post-checkout hook, so it must perform
115
+ # the same asynchronous initialization for a new worktree.
116
+ result = apply_worktree(paths, candidate, setup=True)
114
117
  if result is None:
115
118
  continue
116
119
  print_apply_result(result)
@@ -32,7 +32,7 @@ def test_init_template_is_commented_and_not_an_active_yaml_profile(app_paths):
32
32
  assert "# Port IDs form one contiguous block" in text
33
33
  assert "# Optional local secret files" in text
34
34
  assert "# Optional generated files" in text
35
- assert "# Optional post-checkout initializers" in text
35
+ assert "# Optional worktree initializers" in text
36
36
  assert "$HOME/.config/example-app/frontend.env" in text
37
37
  assert "$HOME/.config/example-app/backend.env" in text
38
38
  assert "cwd: apps/frontend" in text
@@ -55,6 +55,30 @@ def test_reconciler_projects_only_unregistered_worktrees(
55
55
  assert (linked / "generated.env").exists()
56
56
 
57
57
 
58
+ def test_reconciler_runs_initializers_for_unregistered_worktrees(
59
+ app_paths, git_worktrees, monkeypatch
60
+ ):
61
+ main, linked = git_worktrees
62
+ (app_paths.profiles / "example.yaml").write_text(
63
+ "name: example\n"
64
+ f"match:\n main_worktree: {main}\n"
65
+ "ports:\n - id: web\n"
66
+ "init:\n - command: [tool]\n cwd: .\n"
67
+ )
68
+ monkeypatch.setattr("worktree_env.registry.port_is_free", lambda _port: True)
69
+ initialized = []
70
+ monkeypatch.setattr(
71
+ "worktree_env.projector.run_initializers",
72
+ lambda _profile, root: initialized.append(root),
73
+ )
74
+
75
+ reconcile_once(app_paths)
76
+ reconcile_once(app_paths)
77
+
78
+ assert set(initialized) == {main.resolve(), linked.resolve()}
79
+ assert len(initialized) == 2
80
+
81
+
58
82
  def test_launch_agent_watches_common_git_metadata(
59
83
  app_paths, tmp_path, monkeypatch
60
84
  ):
@@ -258,7 +258,7 @@ wheels = [
258
258
 
259
259
  [[package]]
260
260
  name = "worktree-env"
261
- version = "0.3.0"
261
+ version = "0.3.1"
262
262
  source = { editable = "." }
263
263
  dependencies = [
264
264
  { name = "pyyaml" },
File without changes
File without changes
File without changes