carson 4.4.0 → 4.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b22cfd5f4976a5367d0a815fac4f360049673aaa585a3783c7296f155be896a9
4
- data.tar.gz: bdc3f70b4b74dcb489b8dde16ca0244846b4082fc6697713ce83490cb64319c4
3
+ metadata.gz: a8eb2ac9b4d1ac6375fc581b2be86768ad5f441685b36dca3358e31aa43715b8
4
+ data.tar.gz: e2c7e9e3b3a4b35e26c8fbb7d59c46c10db8cf67b01000b0893d528a84055ce1
5
5
  SHA512:
6
- metadata.gz: a77aa24657d4863d5de2a06e14b14e0f339cd9b9ac709933f2b2ec084de721a101781867a7b3a4bb0557623968247ca4333cf5821660778ce0ddd70c20f780ba
7
- data.tar.gz: 5113678e26302f1eb7b9a5c93e59f4a357434ddade3ec9b4eec00f412c1af8b77888b132ab5f849594e04621d0af57e8e9969f3e0292b23b18bb215894a1dac9
6
+ metadata.gz: 58f5388783aa5efbd5a74393c75b6552278bab58d06bc00bcea1a4467353aac70b7aa54685517d02062d61d0befa16898867f82b5ca9c7fc2a0908baa441cf94
7
+ data.tar.gz: d8e760dba0ca676a9a5c6e88d729102fa86f8c9ea205507fbe46821280c42593352bc985d486d6c82b377311cb5564820f06d7d15ddb63e15ee17ebab750780e
data/MANUAL.md CHANGED
@@ -153,6 +153,8 @@ carson worktree create my-feature
153
153
  cd /path/to/.claude/worktrees/my-feature
154
154
  ```
155
155
 
156
+ The worktree lives under the calling harness's agent directory — `.claude/worktrees/` from Claude Code, `.pi/worktrees/` from Pi (detected via `PI_CODING_AGENT`). Set `CARSON_AGENT_DIR` to override. Commands that take a worktree name resolve it across all agent directories, so worktrees created by another harness remain reachable.
157
+
156
158
  On the governed main working tree, Carson blocks raw `git add` / `git commit` and blocks raw `git worktree add/remove`, raw `git pull --rebase`, and raw `gh pr create/merge`. Use `carson worktree create`, `carson sync`, and `carson deliver` instead.
157
159
 
158
160
  **2. Work** — make changes, test them, and either commit normally or let Carson create the delivery commit.
data/README.md CHANGED
@@ -51,6 +51,10 @@ carson onboard your/repo/path
51
51
  carson worktree create your-worktree
52
52
  cd your/repo/path/.claude/worktrees/your-worktree
53
53
 
54
+ # Carson places the worktree under the calling harness's agent directory:
55
+ # .claude/worktrees/ from Claude Code, .pi/worktrees/ from Pi
56
+ # (detected via PI_CODING_AGENT). Set CARSON_AGENT_DIR to override.
57
+
54
58
  # work and test, then commit and hand the branch to Carson
55
59
  git add -A
56
60
  git commit -m "fix: describe this delivery"
data/RELEASE.md CHANGED
@@ -7,6 +7,21 @@ Release-note scope rule:
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 4.5.0
11
+
12
+ ### New
13
+
14
+ - **Harness-aware worktree directories.** `carson checkin` / `carson worktree create` place the worktree under the calling harness's agent directory — `.pi/worktrees/` from a Pi session (detected via `PI_CODING_AGENT`), `.claude/worktrees/` otherwise, exactly as before. Set `CARSON_AGENT_DIR` to override. Bare-name resolution, `.git/info/exclude` bookkeeping, and diagnostics follow the chosen directory, and commands that take a worktree name resolve across all agent directories, so worktrees created by another harness remain reachable.
15
+ - **Sweep and reap cover `.pi/worktrees/`.** Both agent-directory lists gained `.pi`.
16
+
17
+ ### Why
18
+
19
+ The worktree directory was hardcoded to `.claude/worktrees/`, so Pi sessions produced Claude-flavoured paths. Each harness's worktrees now live under their own directory.
20
+
21
+ ### Migration
22
+
23
+ None. Existing `.claude/worktrees/` worktrees resolve by name as before.
24
+
10
25
  ## 4.4.0
11
26
 
12
27
  ### New
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.4.0
1
+ 4.5.0
@@ -8,7 +8,8 @@ module Carson
8
8
 
9
9
  # --- Delegates to Warehouse::Workbench ---
10
10
 
11
- # Creates a new worktree under .claude/worktrees/<name>.
11
+ # Creates a new workbench under the calling harness's agent
12
+ # directory (<agent_dir>/worktrees/<name>, e.g. .pi from a Pi session).
12
13
  def worktree_create!( name:, json_output: false )
13
14
  result = worktree_warehouse.build_workbench!( name: name )
14
15
  finish_worktree( result: result, json_output: json_output )
@@ -10,7 +10,9 @@ module Carson
10
10
  module Workbench
11
11
 
12
12
  # Agent directory names whose workbenches the warehouse may sweep.
13
- AGENT_DIRS = %w[ .claude .codex ].freeze
13
+ # Carson::Worktree::AGENT_DIRS (legacy) carries the same list — keep
14
+ # both in lockstep until reap moves here.
15
+ AGENT_DIRS = %w[ .claude .codex .pi ].freeze
14
16
 
15
17
  # --- Inventory ---
16
18
 
@@ -64,8 +66,9 @@ module Carson
64
66
  end
65
67
 
66
68
  # Resolve a bare name and find the workbench.
67
- # Tries .claude/worktrees/<name> first, then searches all registered
68
- # workbenches by directory name.
69
+ # Tries the calling harness's agent directory first (scoped, then
70
+ # flat), then the other agent directories, then searches all
71
+ # registered workbenches by directory name.
69
72
  def workbench_named( name )
70
73
  if Pathname.new( name ).absolute?
71
74
  return workbench_at( path: name )
@@ -76,18 +79,20 @@ module Carson
76
79
  found = workbench_at( path: relative_candidate )
77
80
  return found if found
78
81
 
79
- # Try scoped path (e.g. "claude/foo" → .claude/worktrees/claude/foo).
82
+ # Try scoped path (e.g. "claude/foo" → .claude/worktrees/claude/foo),
83
+ # the calling harness's directory first.
80
84
  if name.include?( "/" )
81
- scoped_candidate = realpath_safe( File.join( main_worktree_root, ".claude", "worktrees", name ) )
82
- found = workbench_at( path: scoped_candidate )
83
- return found if found
85
+ ordered_agent_dirs.each do |dir|
86
+ found = workbench_at( path: realpath_safe( File.join( main_worktree_root, dir, "worktrees", name ) ) )
87
+ return found if found
88
+ end
84
89
  end
85
90
 
86
- # Try flat layout: .claude/worktrees/<name>.
87
- root = main_worktree_root
88
- candidate = realpath_safe( File.join( root, ".claude", "worktrees", name ) )
89
- found = workbench_at( path: candidate )
90
- return found if found
91
+ # Try flat layout: <agent_dir>/worktrees/<name>, calling harness first.
92
+ ordered_agent_dirs.each do |dir|
93
+ found = workbench_at( path: realpath_safe( File.join( main_worktree_root, dir, "worktrees", name ) ) )
94
+ return found if found
95
+ end
91
96
 
92
97
  # Search all registered workbenches by dirname.
93
98
  matches = workbenches.select { |wb| File.basename( wb.path ) == name }
@@ -105,11 +110,12 @@ module Carson
105
110
  # --- Lifecycle ---
106
111
 
107
112
  # Build a new workbench from local main.
108
- # Creates the directory, branches from the local standard,
109
- # ensures .claude/ is excluded from git status.
113
+ # Creates the directory under the calling harness's agent directory,
114
+ # branches from the local standard, and excludes the agent directory
115
+ # from git status.
110
116
  def build_workbench!( name: )
111
117
  root = main_worktree_root
112
- worktrees_dir = File.join( root, ".claude", "worktrees" )
118
+ worktrees_dir = File.join( root, agent_dir, "worktrees" )
113
119
  workbench_path = File.join( worktrees_dir, name )
114
120
 
115
121
  if Dir.exist?( workbench_path )
@@ -119,8 +125,8 @@ module Carson
119
125
  recovery: "carson worktree remove #{name}, then retry" }
120
126
  end
121
127
 
122
- # Ensure .claude/ is excluded from git status.
123
- ensure_claude_dir_excluded!
128
+ # Ensure the agent directory is excluded from git status.
129
+ ensure_agent_dir_excluded!
124
130
 
125
131
  # Create the worktree with a new branch.
126
132
  FileUtils.mkdir_p( File.dirname( workbench_path ) )
@@ -380,7 +386,7 @@ module Carson
380
386
  wt_list, = git( "worktree", "list", "--porcelain" )
381
387
  branch_list, = git( "branch", "--list", name )
382
388
  git_version, = Open3.capture3( "git", "--version" )
383
- workbench_path = File.join( root, ".claude", "worktrees", name )
389
+ workbench_path = File.join( root, agent_dir, "worktrees", name )
384
390
  entry = workbench_at( path: workbench_path )
385
391
  {
386
392
  git_stdout: git_stdout.to_s.strip,
@@ -395,8 +401,25 @@ module Carson
395
401
  }
396
402
  end
397
403
 
398
- # Ensure .claude/ is in .git/info/exclude.
399
- def ensure_claude_dir_excluded!
404
+ # The calling harness's workbench directory name.
405
+ # CARSON_AGENT_DIR wins over detection; absent detection, .claude is the
406
+ # historical default so Claude Code and Codex sessions behave as before.
407
+ # Pi sessions export PI_CODING_AGENT.
408
+ def agent_dir
409
+ override = ENV.fetch( "CARSON_AGENT_DIR", "" ).to_s.strip
410
+ return override unless override.empty?
411
+ return ".pi" if ENV.key?( "PI_CODING_AGENT" )
412
+
413
+ ".claude"
414
+ end
415
+
416
+ # Agent directories in resolution order: the calling harness's first.
417
+ def ordered_agent_dirs
418
+ ( [ agent_dir ] + AGENT_DIRS ).uniq
419
+ end
420
+
421
+ # Ensure the calling harness's agent directory is in .git/info/exclude.
422
+ def ensure_agent_dir_excluded!
400
423
  git_dir = File.join( main_worktree_root, ".git" )
401
424
  return unless File.directory?( git_dir )
402
425
 
@@ -405,9 +428,10 @@ module Carson
405
428
 
406
429
  FileUtils.mkdir_p( info_dir )
407
430
  existing = File.exist?( exclude_path ) ? File.read( exclude_path ) : ""
408
- return if existing.lines.any? { |line| line.strip == ".claude/" }
431
+ entry = "#{agent_dir}/"
432
+ return if existing.lines.any? { |line| line.strip == entry }
409
433
 
410
- File.open( exclude_path, "a" ) { |file| file.puts ".claude/" }
434
+ File.open( exclude_path, "a" ) { |file| file.puts entry }
411
435
  rescue StandardError
412
436
  # Best-effort — do not block workbench creation.
413
437
  end
@@ -12,7 +12,9 @@ require "pathname"
12
12
  module Carson
13
13
  class Worktree
14
14
  # Agent directory names whose worktrees Carson may sweep.
15
- AGENT_DIRS = %w[ .claude .codex ].freeze
15
+ # Warehouse::Workbench::AGENT_DIRS carries the same list — keep both in
16
+ # lockstep until reap/sweep move to the warehouse.
17
+ AGENT_DIRS = %w[ .claude .codex .pi ].freeze
16
18
 
17
19
  attr_reader :path, :branch, :prunable_reason
18
20
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carson
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.0
4
+ version: 4.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hailei Wang