codexspec 0.7.17__tar.gz → 0.7.18__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 (106) hide show
  1. {codexspec-0.7.17 → codexspec-0.7.18}/PKG-INFO +9 -1
  2. {codexspec-0.7.17 → codexspec-0.7.18}/README.md +8 -0
  3. {codexspec-0.7.17 → codexspec-0.7.18}/pyproject.toml +1 -1
  4. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/create-new-feature.sh +11 -0
  5. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/powershell/create-new-feature.ps1 +28 -0
  6. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/__init__.py +135 -3
  7. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/automation.py +17 -7
  8. codexspec-0.7.18/src/codexspec/worktrees.py +477 -0
  9. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/analyze.md +72 -0
  10. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/auto-dev.md +72 -0
  11. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/blueprint.md +72 -0
  12. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/checklist.md +72 -0
  13. codexspec-0.7.18/templates/commands/clarify.md +159 -0
  14. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/commit-staged.md +72 -0
  15. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/config.md +83 -0
  16. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/constitution.md +72 -0
  17. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/debug.md +72 -0
  18. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/distill.md +72 -0
  19. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/evolve.md +72 -0
  20. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/generate-spec.md +72 -0
  21. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/implement-tasks.md +72 -0
  22. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/onboard.md +72 -0
  23. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/plan-to-tasks.md +72 -0
  24. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/pr.md +72 -0
  25. codexspec-0.7.18/templates/commands/quick.md +141 -0
  26. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/release-notes.md +72 -0
  27. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/reverse-spec.md +79 -7
  28. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/review-code.md +72 -0
  29. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/review-design.md +72 -0
  30. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/review-plan.md +72 -0
  31. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/review-spec.md +72 -0
  32. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/review-tasks.md +72 -0
  33. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/spec-to-design.md +72 -0
  34. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/spec-to-plan.md +72 -0
  35. {codexspec-0.7.17 → codexspec-0.7.18}/templates/commands/specify.md +76 -1
  36. codexspec-0.7.18/templates/commands/tasks-to-issues.md +216 -0
  37. codexspec-0.7.17/templates/commands/clarify.md +0 -87
  38. codexspec-0.7.17/templates/commands/quick.md +0 -69
  39. codexspec-0.7.17/templates/commands/tasks-to-issues.md +0 -144
  40. {codexspec-0.7.17 → codexspec-0.7.18}/.gitignore +0 -0
  41. {codexspec-0.7.17 → codexspec-0.7.18}/LICENSE +0 -0
  42. {codexspec-0.7.17 → codexspec-0.7.18}/codexspec-icon.svg +0 -0
  43. {codexspec-0.7.17 → codexspec-0.7.18}/codexspec-logo-dark.svg +0 -0
  44. {codexspec-0.7.17 → codexspec-0.7.18}/codexspec-logo-light.svg +0 -0
  45. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/check-i18n-completeness.sh +0 -0
  46. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/check-i18n-structure.sh +0 -0
  47. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/check-prerequisites.sh +0 -0
  48. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/common.sh +0 -0
  49. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/bash/review-context.sh +0 -0
  50. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/powershell/check-prerequisites.ps1 +0 -0
  51. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/powershell/common.ps1 +0 -0
  52. {codexspec-0.7.17 → codexspec-0.7.18}/scripts/powershell/review-context.ps1 +0 -0
  53. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/blueprint.py +0 -0
  54. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/commands/__init__.py +0 -0
  55. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/commands/installer.py +0 -0
  56. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/__init__.py +0 -0
  57. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/app.js +0 -0
  58. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/ar.json +0 -0
  59. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/de.json +0 -0
  60. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/en.json +0 -0
  61. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/es.json +0 -0
  62. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/fr.json +0 -0
  63. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/hi.json +0 -0
  64. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/it.json +0 -0
  65. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/ja.json +0 -0
  66. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/ko.json +0 -0
  67. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/pt.json +0 -0
  68. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/ru.json +0 -0
  69. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/zh-CN.json +0 -0
  70. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/i18n/zh-TW.json +0 -0
  71. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/index.html +0 -0
  72. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/assets/styles.css +0 -0
  73. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/domain.py +0 -0
  74. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/lease.py +0 -0
  75. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/models.py +0 -0
  76. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/records.py +0 -0
  77. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/server.py +0 -0
  78. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/session.py +0 -0
  79. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/terminal.py +0 -0
  80. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/distill_review/transaction.py +0 -0
  81. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/i18n.py +0 -0
  82. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/idea.md +0 -0
  83. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/integrations/__init__.py +0 -0
  84. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/integrations/base.py +0 -0
  85. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/integrations/claude.py +0 -0
  86. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/integrations/codex.py +0 -0
  87. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/profile.py +0 -0
  88. {codexspec-0.7.17 → codexspec-0.7.18}/src/codexspec/translator.py +0 -0
  89. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/checklist-template.md +0 -0
  90. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/constitution-template.md +0 -0
  91. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/design-template.md +0 -0
  92. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/plan-template-detailed.md +0 -0
  93. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/plan-template-simple.md +0 -0
  94. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/requirements-template.md +0 -0
  95. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/spec-template-detailed.md +0 -0
  96. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/spec-template-simple.md +0 -0
  97. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/tasks-template-detailed.md +0 -0
  98. {codexspec-0.7.17 → codexspec-0.7.18}/templates/docs/tasks-template-simple.md +0 -0
  99. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/de.json +0 -0
  100. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/en.json +0 -0
  101. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/es.json +0 -0
  102. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/fr.json +0 -0
  103. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/ja.json +0 -0
  104. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/ko.json +0 -0
  105. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/pt-BR.json +0 -0
  106. {codexspec-0.7.17 → codexspec-0.7.18}/templates/translations/zh-CN.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codexspec
3
- Version: 0.7.17
3
+ Version: 0.7.18
4
4
  Summary: CodexSpec - A Requirements-First SDD toolkit for Claude Code
5
5
  Project-URL: Homepage, https://github.com/Zts0hg/codexspec
6
6
  Project-URL: Repository, https://github.com/Zts0hg/codexspec
@@ -565,6 +565,7 @@ Implementation follows **conditional TDD workflow**:
565
565
  | `--list-langs` | List all supported languages |
566
566
  | `--auto-next` | Toggle/set `workflow.auto_next` (bare toggles; or on/off) |
567
567
  | `--auto-distill` | Toggle/set `workflow.auto_distill` (default on; bare toggles; or on/off) |
568
+ | `--worktrees` | Toggle/set checkout-local worktree isolation (default on; bare toggles; or on/off) |
568
569
 
569
570
  </details>
570
571
 
@@ -837,3 +838,10 @@ MIT License - see [LICENSE](LICENSE) for details.
837
838
 
838
839
  - Inspired by [GitHub spec-kit](https://github.com/github/spec-kit)
839
840
  - Built for [Claude Code](https://claude.ai/code)
841
+
842
+ Ordinary features develop in separate worktrees under the main checkout's sibling
843
+ `<repository-name>-codexspec-worktrees/`, starting before the first requirements write.
844
+ Blueprint and auto-dev retain their fixed shared worktree. Standalone project edits
845
+ use a maintenance worktree; settings take effect per checkout and reach other checkouts
846
+ through Git integration. See [worktree development](docs/en/user-guide/workflow.md#worktree-development)
847
+ and [configuration](docs/en/reference/configuration.md#workflowworktrees).
@@ -520,6 +520,7 @@ Implementation follows **conditional TDD workflow**:
520
520
  | `--list-langs` | List all supported languages |
521
521
  | `--auto-next` | Toggle/set `workflow.auto_next` (bare toggles; or on/off) |
522
522
  | `--auto-distill` | Toggle/set `workflow.auto_distill` (default on; bare toggles; or on/off) |
523
+ | `--worktrees` | Toggle/set checkout-local worktree isolation (default on; bare toggles; or on/off) |
523
524
 
524
525
  </details>
525
526
 
@@ -792,3 +793,10 @@ MIT License - see [LICENSE](LICENSE) for details.
792
793
 
793
794
  - Inspired by [GitHub spec-kit](https://github.com/github/spec-kit)
794
795
  - Built for [Claude Code](https://claude.ai/code)
796
+
797
+ Ordinary features develop in separate worktrees under the main checkout's sibling
798
+ `<repository-name>-codexspec-worktrees/`, starting before the first requirements write.
799
+ Blueprint and auto-dev retain their fixed shared worktree. Standalone project edits
800
+ use a maintenance worktree; settings take effect per checkout and reach other checkouts
801
+ through Git integration. See [worktree development](docs/en/user-guide/workflow.md#worktree-development)
802
+ and [configuration](docs/en/reference/configuration.md#workflowworktrees).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codexspec"
3
- version = "0.7.17"
3
+ version = "0.7.18"
4
4
  description = "CodexSpec - A Requirements-First SDD toolkit for Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -50,6 +50,17 @@ if [ -z "$FEATURE_SUFFIX" ]; then
50
50
  exit 1
51
51
  fi
52
52
 
53
+ # Resolve the switch before creating directories or changing Git state.
54
+ if ! command_exists codexspec; then
55
+ log_error "CodexSpec runtime is unavailable. Install codexspec before creating a feature."
56
+ exit 1
57
+ fi
58
+ WORKTREE_SETTING=$(codexspec _worktree-helper setting) || exit $?
59
+ case "$WORKTREE_SETTING" in
60
+ *'"enabled": false'*) ;; # Explicit opt-out retains the original in-place flow below.
61
+ *) exec codexspec _worktree-helper create --name "$FEATURE_SUFFIX" ;;
62
+ esac
63
+
53
64
  TIMESTAMP=$(date +"%Y-%m%d-%H%M")
54
65
  RANDOM_SUFFIX=$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 2)
55
66
  FEATURE_ID="${TIMESTAMP}${RANDOM_SUFFIX}"
@@ -132,6 +132,34 @@ if (-not $branchSuffix) {
132
132
  exit 1
133
133
  }
134
134
 
135
+ # Resolve isolation before any feature directory or branch mutation.
136
+ if (-not (Get-Command codexspec -ErrorAction SilentlyContinue)) {
137
+ Write-Error "CodexSpec runtime is unavailable. Install codexspec before creating a feature."
138
+ exit 1
139
+ }
140
+ $settingOutput = & codexspec _worktree-helper setting
141
+ if ($LASTEXITCODE -ne 0) { Write-Output $settingOutput; exit $LASTEXITCODE }
142
+ $setting = $settingOutput | ConvertFrom-Json
143
+ if ($setting.enabled) {
144
+ $workspaceOutput = & codexspec _worktree-helper create --name $branchSuffix
145
+ $workspaceExit = $LASTEXITCODE
146
+ if ($workspaceExit -ne 0) { Write-Output $workspaceOutput; exit $workspaceExit }
147
+ $workspace = $workspaceOutput | ConvertFrom-Json
148
+ $env:CODEXSPEC_FEATURE = $workspace.branch
149
+ $result = [PSCustomObject]@{
150
+ BRANCH_NAME = $workspace.branch
151
+ REQUIREMENTS_FILE = $workspace.requirements_file
152
+ FEATURE_ID = $workspace.feature_id
153
+ WORKTREE_PATH = $workspace.workspace
154
+ HAS_GIT = $true
155
+ STATUS = $workspace.status
156
+ WARNINGS = $workspace.warnings
157
+ }
158
+ if ($Json) { $result | ConvertTo-Json -Compress }
159
+ else { $result | Format-List | Out-String | Write-Output }
160
+ exit 0
161
+ }
162
+
135
163
  $specsDir = Join-Path $repoRoot '.codexspec/specs'
136
164
  New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
137
165
 
@@ -14,7 +14,7 @@ import sys
14
14
  import webbrowser
15
15
  from datetime import datetime
16
16
  from pathlib import Path
17
- from typing import Optional
17
+ from typing import Any, Optional
18
18
 
19
19
  import typer
20
20
  from rich.console import Console
@@ -64,9 +64,18 @@ from .i18n import (
64
64
  from .integrations import get_integrations
65
65
  from .profile import ensure_profile_scaffold, inject_profile_block
66
66
  from .translator import SUPPORTED_LANGUAGES, translate
67
+ from .worktrees import (
68
+ MAINTENANCE_NAME,
69
+ WorkspaceManager,
70
+ feature_name,
71
+ read_worktrees,
72
+ validate_write_path,
73
+ write_destination,
74
+ write_worktrees,
75
+ )
67
76
 
68
77
  # Version info
69
- __version__ = "0.7.17"
78
+ __version__ = "0.7.18"
70
79
  __author__ = "CodexSpec Team"
71
80
 
72
81
  # Constitution file path constants
@@ -82,6 +91,51 @@ app = typer.Typer(
82
91
  console = Console()
83
92
 
84
93
 
94
+ @app.command("_worktree-helper", hidden=True)
95
+ def worktree_helper(
96
+ action: str = typer.Argument(...),
97
+ name: Optional[str] = typer.Option(None, "--name"),
98
+ feature: Optional[str] = typer.Option(None, "--feature"),
99
+ verification: Optional[Path] = typer.Option(None, "--verification"),
100
+ ) -> None:
101
+ """Return structured workspace facts; never emit shell code for callers to evaluate."""
102
+ recovery: dict[str, Any] = {}
103
+ result: dict[str, Any]
104
+ try:
105
+ from .worktrees import checkout_root
106
+
107
+ root = checkout_root(Path.cwd())
108
+ if action == "setting":
109
+ result = {"enabled": read_worktrees(root / ".codexspec/config.yml")}
110
+ elif action == "route":
111
+ result = {"status": "ready", "workspace": str(write_destination(root))}
112
+ elif action == "create" and not read_worktrees(root / ".codexspec/config.yml"):
113
+ result = {"status": "disabled", "workspace": str(root.resolve())}
114
+ else:
115
+ manager = WorkspaceManager(root)
116
+ if action == "create":
117
+ if bool(name) == bool(feature):
118
+ raise AutomationError("expected_name_or_feature")
119
+ identity = feature if feature else feature_name(name or "")
120
+ branch, path, _ = manager._identity(identity)
121
+ recovery = {"branch": branch, "workspace": str(path)}
122
+ result = manager.create(identity, reuse=feature is not None)
123
+ elif action == "resolve" and feature:
124
+ result = manager.resolve(feature)
125
+ elif action == "maintenance":
126
+ result = manager.create(MAINTENANCE_NAME)
127
+ elif action == "finish" and feature and verification:
128
+ result = manager.finish(feature, json.loads(verification.read_text(encoding="utf-8")))
129
+ else:
130
+ raise AutomationError("invalid_workspace_action", action)
131
+ print(json.dumps(result, ensure_ascii=False))
132
+ if result.get("status") in {"creating", "merge_requires_resolution", "merge_requires_verification"}:
133
+ raise typer.Exit(3)
134
+ except (AutomationError, OSError, ValueError) as exc:
135
+ print(json.dumps({**recovery, "status": "blocked", "error": str(exc)}, ensure_ascii=False))
136
+ raise typer.Exit(1) from exc
137
+
138
+
85
139
  @app.command("_distill-review-helper", hidden=True)
86
140
  def distill_review_helper(
87
141
  project_root: Path = typer.Option(Path("."), "--project-root"),
@@ -99,6 +153,17 @@ def distill_review_helper(
99
153
  if not root.is_dir() or not (root / ".codexspec" / "profile").is_dir():
100
154
  _review_result(language, {"status": "invalid_input", "error": "invalid_project_root"})
101
155
  raise typer.Exit(2)
156
+ try:
157
+ root = write_destination(root)
158
+ except (AutomationError, OSError) as exc:
159
+ _review_result(language, {"status": "invalid_input", "error": str(exc)})
160
+ raise typer.Exit(2) from exc
161
+ language = get_interaction_language(root / ".codexspec" / "config.yml")
162
+ if not (root / ".codexspec" / "profile").is_dir():
163
+ _review_result(
164
+ language, {"status": "invalid_input", "error": "missing_destination_profile", "project_root": str(root)}
165
+ )
166
+ raise typer.Exit(2)
102
167
  store = SessionStore(root)
103
168
  lease = ReviewLease(root)
104
169
  try:
@@ -526,6 +591,9 @@ def config(
526
591
  "--auto-distill",
527
592
  help="Toggle workflow.auto_distill (bare), or set it (on/off|true/false|1/0|yes/no).",
528
593
  ),
594
+ worktrees: Optional[str] = typer.Option(
595
+ None, "--worktrees", help="Toggle checkout-local worktree isolation (default on), or set on/off."
596
+ ),
529
597
  ) -> None:
530
598
  """
531
599
  View or modify CodexSpec project configuration.
@@ -561,6 +629,58 @@ def config(
561
629
  console.print("Run [cyan]codexspec init[/cyan] to create a new project.")
562
630
  raise typer.Exit(1)
563
631
 
632
+ # Validate values before creating any destination workspace.
633
+ for value, sentinel, option in (
634
+ (auto_next, _AUTO_NEXT_SENTINEL, "--auto-next"),
635
+ (auto_distill, _AUTO_DISTILL_SENTINEL, "--auto-distill"),
636
+ (worktrees, "__toggle_worktrees__", "--worktrees"),
637
+ ):
638
+ if value is not None and value != sentinel:
639
+ try:
640
+ parse_auto_next_value(value)
641
+ except ValueError:
642
+ console.print(f"[red]Invalid {option} value:[/red] {value!r}")
643
+ raise typer.Exit(1)
644
+ modifying = any(
645
+ value is not None
646
+ for value in (
647
+ auto_next,
648
+ auto_distill,
649
+ worktrees,
650
+ set_lang,
651
+ set_commit_lang,
652
+ set_interaction_lang,
653
+ set_document_lang,
654
+ )
655
+ )
656
+ if modifying:
657
+ try:
658
+ isolated = read_worktrees(config_file)
659
+ destination = write_destination(config_file.parent.parent)
660
+ config_file = destination / ".codexspec" / "config.yml"
661
+ if isolated:
662
+ validate_write_path(destination, config_file)
663
+ if set_lang is not None or set_interaction_lang is not None:
664
+ _validate_command_frontmatter(destination)
665
+ if not config_file.is_file():
666
+ raise AutomationError("missing_destination_config", str(config_file))
667
+ console.print(f"Configuration destination: {config_file}", markup=False, soft_wrap=True)
668
+ console.print("Changes apply to this checkout; other checkouts receive them through Git integration.")
669
+ except (AutomationError, OSError) as exc:
670
+ console.print(str(exc), markup=False)
671
+ raise typer.Exit(1) from exc
672
+ if worktrees is not None:
673
+ target = (
674
+ not read_worktrees(config_file) if worktrees == "__toggle_worktrees__" else parse_auto_next_value(worktrees)
675
+ )
676
+ try:
677
+ write_worktrees(config_file, target)
678
+ except (AutomationError, OSError) as exc:
679
+ console.print(str(exc), markup=False)
680
+ raise typer.Exit(1) from exc
681
+ console.print(f"workflow.worktrees = {str(target).lower()}")
682
+ return
683
+
564
684
  # Handle auto-next toggle/set
565
685
  if auto_next is not None:
566
686
  if auto_next == _AUTO_NEXT_SENTINEL:
@@ -740,6 +860,7 @@ def config(
740
860
  return
741
861
 
742
862
  # Display current configuration
863
+ console.print(f"Effective workflow.worktrees: {str(read_worktrees(config_file)).lower()}")
743
864
  console.print(
744
865
  Panel(
745
866
  config_file.read_text(encoding="utf-8"),
@@ -1228,6 +1349,15 @@ def init(
1228
1349
  console.print(f"[yellow]{_important_action(integrations, normalized_lang)}[/yellow]")
1229
1350
 
1230
1351
 
1352
+ def _validate_command_frontmatter(root: Path) -> None:
1353
+ commands = root / ".claude" / "commands" / COMMANDS_SUBDIR
1354
+ validate_write_path(root, commands)
1355
+ if commands.exists() and not commands.is_dir():
1356
+ raise AutomationError("unsafe_write_path", str(commands))
1357
+ for command in commands.glob("*.md"):
1358
+ validate_write_path(root, command)
1359
+
1360
+
1231
1361
  def _rerender_command_frontmatter(config_file: Path) -> None:
1232
1362
  """Re-render installed command frontmatter in the current interaction language.
1233
1363
 
@@ -1237,7 +1367,7 @@ def _rerender_command_frontmatter(config_file: Path) -> None:
1237
1367
  pre-translated cache are not re-rendered here (that path would invoke the
1238
1368
  ``claude`` CLI once per command); the user is advised to run ``codexspec init``.
1239
1369
  """
1240
- commands_subdir = Path.cwd() / ".claude" / "commands" / COMMANDS_SUBDIR
1370
+ commands_subdir = config_file.parent.parent / ".claude" / "commands" / COMMANDS_SUBDIR
1241
1371
  if not commands_subdir.exists():
1242
1372
  return
1243
1373
  language = get_interaction_language(config_file) # re-read AFTER the config write
@@ -1919,6 +2049,8 @@ def main() -> None:
1919
2049
  sys.argv = _normalize_auto_next_argv(sys.argv)
1920
2050
  if "--auto-distill" in sys.argv:
1921
2051
  sys.argv = _normalize_auto_distill_argv(sys.argv)
2052
+ if "--worktrees" in sys.argv:
2053
+ sys.argv = _normalize_optional_value_argv(sys.argv, "--worktrees", "__toggle_worktrees__")
1922
2054
  app()
1923
2055
 
1924
2056
 
@@ -173,8 +173,12 @@ def locate_repository(cwd: Path | str, *, runner: GitRunner | None = None) -> Re
173
173
  current = Path(cwd).resolve()
174
174
  top = git.run(current, "rev-parse", "--show-toplevel", check=False)
175
175
  if top.returncode != 0:
176
- raise AutomationError("not_git_repository")
177
- invoking_root = Path(top.stdout.strip()).resolve()
176
+ bare = git.run(current, "rev-parse", "--is-bare-repository", check=False)
177
+ if bare.returncode != 0 or bare.stdout.strip() != "true":
178
+ raise AutomationError("not_git_repository")
179
+ invoking_root = Path(git.run(current, "rev-parse", "--absolute-git-dir").stdout.strip()).resolve()
180
+ else:
181
+ invoking_root = Path(top.stdout.strip()).resolve()
178
182
  worktrees = _parse_worktrees(git.run(invoking_root, "worktree", "list", "--porcelain").stdout)
179
183
  if not worktrees:
180
184
  raise AutomationError("worktree_registry_empty")
@@ -188,7 +192,7 @@ def locate_repository(cwd: Path | str, *, runner: GitRunner | None = None) -> Re
188
192
  default_branch = remote_branch or _local_default_branch(git, primary_root, worktrees[0].get("branch"))
189
193
  if remote_name and remote_ref is None:
190
194
  remote_ref = f"refs/remotes/{remote_name}/{default_branch}"
191
- worktree_path = Path(f"{primary_root}-worktrees") / WORKTREE_BASENAME
195
+ worktree_path = Path(f"{primary_root}-codexspec-worktrees") / WORKTREE_BASENAME
192
196
  return RepositoryContext(
193
197
  repository_root=primary_root,
194
198
  common_git_dir=common_git_dir,
@@ -303,16 +307,22 @@ def _remote_default(git: GitRunner, root: Path) -> tuple[str | None, str | None,
303
307
  if symbolic.returncode != 0:
304
308
  return remote, None, None
305
309
  ref = symbolic.stdout.strip()
306
- return remote, ref, ref.rsplit("/", 1)[-1]
310
+ return remote, ref, ref.removeprefix(f"refs/remotes/{remote}/")
307
311
 
308
312
 
309
313
  def _local_default_branch(git: GitRunner, root: Path, primary_branch_ref: str | None) -> str:
310
314
  for candidate in ("main", "master"):
311
315
  if git.run(root, "show-ref", "--verify", "--quiet", f"refs/heads/{candidate}", check=False).returncode == 0:
312
316
  return candidate
313
- if primary_branch_ref and primary_branch_ref.startswith("refs/heads/"):
314
- return primary_branch_ref.removeprefix("refs/heads/")
315
- raise AutomationError("default_branch_not_found")
317
+ configured = git.run(root, "config", "--get", "init.defaultBranch", check=False).stdout.strip()
318
+ if (
319
+ configured
320
+ and git.run(root, "show-ref", "--verify", "--quiet", f"refs/heads/{configured}", check=False).returncode == 0
321
+ ):
322
+ return configured
323
+ raise AutomationError(
324
+ "default_branch_not_found", "Configure the remote default branch or a valid init.defaultBranch."
325
+ )
316
326
 
317
327
 
318
328
  def _parse_worktrees(output: str) -> list[dict[str, Any]]: