diffbroker 0.13.1 → 0.14.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: 9fc1c3a662ccc736b0c62b0d083dbac979f52cf13f1c15e7eb5e12c009bdefe5
4
- data.tar.gz: a2b6d513455ba9439a99b67603b9507b83aa3e510c323b2c94cf8748b5157e96
3
+ metadata.gz: 432bb946c0772a58cf38c9234bc6f717c79f4aa727aad1bebb7b07f17c9c5909
4
+ data.tar.gz: 812611ec01f56bb33748565704829e4cc0b46267319d893a161ff88e0b5ee189
5
5
  SHA512:
6
- metadata.gz: 4dde5f0960aa3d14efcdc9e8c301a997bce559dff9ff14e836c4af2cbb5d5dcd4b8c35892b88a354aa0d30c593158c217f0e5559cf18456bd5531b1a3523bd53
7
- data.tar.gz: acd78b508a85c294dab674659e7ed72caa263f712e688ea6006169ea92293106f19136547b7aac70b1be437c60eff80357c8f89e182689e18888518821a60807
6
+ metadata.gz: c748db09f1bba8074c246fa20634dddd77ad96603441f82402ca9b42302cbc5df5b21913bea1b2900af1b2130bf7bc2a3b4cd1d85ae325ea5ce828705136d728
7
+ data.tar.gz: fa95a2a4958005c0d49858e20bc71b840686ec02b12eeb7117f95710a83a049e8333dd3939667e4698a0f5589886f7d808aeee2099cabf286958a41f1177baf9
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffbroker
4
+ # What `diffbroker status` says about folders the scan was refused: the folders and, on macOS, the steps that give the
5
+ # program access (Full Disk Access). Devices says the same in the browser (devices/_scan_roots). Which program needs the
6
+ # grant depends on how the companion runs, so both are named: the Ruby that runs the background service and the terminal.
7
+ module AccessHelp
8
+ module_function
9
+
10
+ def lines(paths, macos: RbConfig::CONFIG["host_os"].to_s.include?("darwin"), ruby: ruby_path)
11
+ return [] if paths.empty?
12
+
13
+ lines = [ "Folders the system would not let the companion read (their repositories are not found):" ]
14
+ lines += paths.map { " #{_1}" }
15
+ lines += macos ? macos_steps(ruby) : [ " Check who may read them (`ls -ld <folder>`): the companion runs as #{ENV["USER"] || "your user"}." ]
16
+ lines
17
+ end
18
+
19
+ def macos_steps(ruby)
20
+ [
21
+ "To allow it (macOS):",
22
+ " 1. Open System Settings > Privacy & Security > Full Disk Access.",
23
+ " 2. Click + and add the program that runs the companion: #{ruby}",
24
+ " (in the file picker press Cmd+Shift+G to type a path). A companion you start by hand in a terminal uses that",
25
+ " terminal app's access instead: add the terminal there.",
26
+ " 3. Run `diffbroker restart`.",
27
+ "Or keep your code outside cloud folders (Dropbox, iCloud Drive), which macOS protects."
28
+ ]
29
+ end
30
+
31
+ def ruby_path = File.realpath(RbConfig.ruby)
32
+ rescue SystemCallError
33
+ RbConfig.ruby
34
+ end
35
+ end
@@ -303,9 +303,11 @@ module Diffbroker
303
303
  @out.puts "Roots: #{config.roots.join(", ")} (plus the projects Claude Code and Codex know)"
304
304
  @out.puts "Worktrees: #{config.worktree_root}"
305
305
  @out.puts "Task worktrees: #{Worktree.new(root: config.worktree_root).list.size} (running or kept; `diffbroker clean` removes finished ones)"
306
- repositories = config.repo_scanner.scan
306
+ scanner = config.repo_scanner
307
+ repositories = scanner.scan
307
308
  @out.puts "Repositories found: #{repositories.size}"
308
309
  repositories.each { @out.puts " #{_1["full_name"]} #{_1["path"]}" }
310
+ AccessHelp.lines(scanner.unreadable).each { @out.puts _1 }
309
311
  end
310
312
 
311
313
  # What the daemon wrote about itself (DaemonState), next to what this installed version and service would run.
@@ -242,7 +242,9 @@ module Diffbroker
242
242
  def rescan(client)
243
243
  @repositories = @scanner.scan
244
244
  @scanned_at = @clock.call
245
- client.perform("repositories", "repositories" => @repositories)
245
+ payload = { "repositories" => @repositories }
246
+ payload["unreadable"] = @scanner.unreadable if @scanner.respond_to?(:unreadable) # Devices explains how to grant access
247
+ client.perform("repositories", payload)
246
248
  end
247
249
 
248
250
  # The plan limits change slowly and reading them can wait on the network, so: every few minutes, off the tick's thread.
@@ -21,6 +21,11 @@ module Diffbroker
21
21
 
22
22
  # `exclude` (the task worktrees) is skipped everywhere. `ignored` (folders the user removed) is skipped by everything
23
23
  # found by itself; `explicit_roots` (folders the user added) are scanned even inside an ignored folder.
24
+ # Folders the last scan was refused (macOS answers EPERM for Dropbox, iCloud, Desktop and the like until the program has
25
+ # been given access; other systems say EACCES). Reported to the server so Devices can explain how to grant it.
26
+ MAX_UNREADABLE = 20
27
+ attr_reader :unreadable
28
+
24
29
  def initialize(roots:, depth: 3, exclude: [], hints: [], home: Dir.home, explicit_roots: [], ignored: [])
25
30
  @roots = roots
26
31
  @explicit_roots = explicit_roots
@@ -30,10 +35,12 @@ module Diffbroker
30
35
  @depth = depth
31
36
  @exclude = exclude.map { File.expand_path(_1) }
32
37
  @skip = @exclude + @ignored
38
+ @unreadable = []
33
39
  end
34
40
 
35
41
  def scan
36
42
  found = {}
43
+ @unreadable = []
37
44
  @roots.each { |root| walk(File.expand_path(root), 0, found, @depth, @skip) if File.directory?(root) }
38
45
  @explicit_roots.each { |root| walk(File.expand_path(root), 0, found, @depth, @exclude) if File.directory?(root) }
39
46
  parents = @hints.filter_map { |hint| add_hint(File.expand_path(hint), found) }.map { File.dirname(_1) }.uniq
@@ -79,8 +86,10 @@ module Diffbroker
79
86
  path = File.join(dir, child)
80
87
  walk(path, level + 1, found, limit, skip) if File.directory?(path) && !File.symlink?(path)
81
88
  end
89
+ rescue Errno::EPERM, Errno::EACCES
90
+ @unreadable << dir if @unreadable.size < MAX_UNREADABLE && !@unreadable.include?(dir)
82
91
  rescue SystemCallError
83
- nil # a folder that cannot be listed (macOS answers EPERM for Dropbox, iCloud and other protected folders) holds nothing for us
92
+ nil # gone meanwhile, or not a folder: nothing to report
84
93
  end
85
94
 
86
95
  def remote_names(dir) = GitContext.remote_urls(dir).filter_map { |_name, url| self.class.full_name_from(url) }.uniq
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Diffbroker
4
- VERSION = "0.13.1"
4
+ VERSION = "0.14.0"
5
5
  end
data/lib/diffbroker.rb CHANGED
@@ -32,6 +32,7 @@ module Diffbroker
32
32
  autoload :Launcher, File.expand_path("diffbroker/launcher", __dir__)
33
33
  autoload :Maintenance, File.expand_path("diffbroker/maintenance", __dir__)
34
34
  autoload :LocalRelay, File.expand_path("diffbroker/local_relay", __dir__)
35
+ autoload :AccessHelp, File.expand_path("diffbroker/access_help", __dir__)
35
36
  autoload :Login, File.expand_path("diffbroker/login", __dir__)
36
37
  autoload :Neutralizer, File.expand_path("diffbroker/neutralizer", __dir__)
37
38
  autoload :PlanUsage, File.expand_path("diffbroker/plan_usage", __dir__)
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "diffbroker",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Skills for working on Diff Broker agent tasks: the diffbroker MCP tools, write rules and report_task."
5
5
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diffbroker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Sidorov (@newstler)
@@ -34,6 +34,7 @@ files:
34
34
  - LICENSE.txt
35
35
  - exe/diffbroker
36
36
  - lib/diffbroker.rb
37
+ - lib/diffbroker/access_help.rb
37
38
  - lib/diffbroker/agent_projects.rb
38
39
  - lib/diffbroker/agents.rb
39
40
  - lib/diffbroker/agents/base.rb