claude-inbox 0.12.1 → 0.12.2
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 +4 -4
- data/lib/claude_inbox/settings.rb +23 -8
- data/lib/claude_inbox/slash_commands.rb +9 -7
- data/lib/claude_inbox/subprocess.rb +16 -4
- data/lib/claude_inbox.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39ef2bafdac9f7b1c8ba1d4e6f28f5105a5fd5c42320f627caf61fd07f9c314d
|
|
4
|
+
data.tar.gz: 0ed5aadb2d02b61b1eabca62a7de74820f7e884f5ffdc6f0bbb68382ba26cc13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 393709c2bc4fc083ded424de49ca3ad102bcf41d75fd72b5c445d8b1f23f7e80f7df0ac1623d4081994e14699168db7c58a2bc64f486b00dd83941c2d691600f
|
|
7
|
+
data.tar.gz: fb06b9a05422bff8e3a5474169a11574041219ae700e30a5178a1e507e74df95cd0c069a1c395a070a7f7856bbe6bcdd6380c2a6011a03ff36a1210c05cdae2e
|
|
@@ -3,17 +3,15 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
module ClaudeInbox
|
|
6
|
-
# What `claude`
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# What a `claude` session in a directory gets from the settings files it
|
|
7
|
+
# reads, most specific wins: the defaults for flags left off, and the
|
|
8
|
+
# plugins it loads.
|
|
9
9
|
module Settings
|
|
10
|
+
# Strings, or nil when nothing sets them: then the CLI decides at launch.
|
|
10
11
|
Defaults = Struct.new(:model, :effort, :permission_mode, :remote)
|
|
11
12
|
|
|
12
13
|
def self.defaults(cwd, home: Dir.home)
|
|
13
|
-
user =
|
|
14
|
-
project = read(File.join(cwd, ".claude", "settings.json"))
|
|
15
|
-
local = read(File.join(cwd, ".claude", "settings.local.json"))
|
|
16
|
-
layers = [user, project, local]
|
|
14
|
+
user, project, local = layers = layers(cwd, home)
|
|
17
15
|
Defaults.new(
|
|
18
16
|
model: pick(layers, "model"),
|
|
19
17
|
effort: pick(layers, "effortLevel"),
|
|
@@ -22,6 +20,23 @@ module ClaudeInbox
|
|
|
22
20
|
)
|
|
23
21
|
end
|
|
24
22
|
|
|
23
|
+
# The "name@marketplace" keys of the plugins a session in cwd loads.
|
|
24
|
+
# Its install scope plays no part; see docs/cli-quirks.md.
|
|
25
|
+
def self.enabled_plugins(cwd, home: Dir.home)
|
|
26
|
+
layers(cwd, home).each_with_object({}) do |h, on|
|
|
27
|
+
plugins = h["enabledPlugins"]
|
|
28
|
+
on.merge!(plugins) if plugins.is_a?(Hash)
|
|
29
|
+
end.select { |_, v| v == true }.keys
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.layers(cwd, home)
|
|
33
|
+
[
|
|
34
|
+
read(File.join(home, ".claude", "settings.json")),
|
|
35
|
+
read(File.join(cwd, ".claude", "settings.json")),
|
|
36
|
+
read(File.join(cwd, ".claude", "settings.local.json"))
|
|
37
|
+
]
|
|
38
|
+
end
|
|
39
|
+
|
|
25
40
|
# "Enable Remote Control for all sessions" in /config. The CLI lets a
|
|
26
41
|
# repo turn it off but only user scope turn it on, and still honours the
|
|
27
42
|
# copy older versions kept in ~/.claude.json.
|
|
@@ -47,6 +62,6 @@ module ClaudeInbox
|
|
|
47
62
|
end
|
|
48
63
|
nil
|
|
49
64
|
end
|
|
50
|
-
private_class_method :remote_control, :pick
|
|
65
|
+
private_class_method :layers, :remote_control, :pick
|
|
51
66
|
end
|
|
52
67
|
end
|
|
@@ -6,10 +6,11 @@ module ClaudeInbox
|
|
|
6
6
|
# The slash commands `claude` would offer at its own prompt, read from the
|
|
7
7
|
# same places it reads them: `.claude/commands/*.md` and
|
|
8
8
|
# `.claude/skills/*/SKILL.md` under the project and the home directory,
|
|
9
|
-
# the skills and commands of
|
|
10
|
-
# the skills claude.ai syncs down
|
|
11
|
-
# live inside the CLI and are not
|
|
12
|
-
# the prompt goes to `claude --bg`
|
|
9
|
+
# the skills and commands of the plugins enabled there (named
|
|
10
|
+
# `plugin:name`), and the skills claude.ai syncs down
|
|
11
|
+
# (`anthropic-skills:name`). Built-ins live inside the CLI and are not
|
|
12
|
+
# listed; typing one still works, since the prompt goes to `claude --bg`
|
|
13
|
+
# verbatim.
|
|
13
14
|
#
|
|
14
15
|
# Pure: filesystem in, plain values out. Nothing here runs `claude`.
|
|
15
16
|
module SlashCommands
|
|
@@ -27,7 +28,7 @@ module ClaudeInbox
|
|
|
27
28
|
[
|
|
28
29
|
*from_dir(File.join(cwd, ".claude")),
|
|
29
30
|
*from_dir(File.join(home, ".claude")),
|
|
30
|
-
*plugins(home),
|
|
31
|
+
*plugins(cwd, home),
|
|
31
32
|
*synced(home)
|
|
32
33
|
].uniq(&:name).sort_by(&:name)
|
|
33
34
|
end
|
|
@@ -65,9 +66,10 @@ module ClaudeInbox
|
|
|
65
66
|
|
|
66
67
|
# `installed_plugins.json` maps "name@marketplace" to where the plugin
|
|
67
68
|
# was unpacked: one entry, or a list of them, one per install scope.
|
|
68
|
-
def plugins(home)
|
|
69
|
+
def plugins(cwd, home)
|
|
69
70
|
installed = Settings.read(File.join(home, ".claude", "plugins", "installed_plugins.json"))
|
|
70
|
-
|
|
71
|
+
enabled = Settings.enabled_plugins(cwd, home:)
|
|
72
|
+
(installed["plugins"] || {}).slice(*enabled).flat_map do |key, entries|
|
|
71
73
|
plugin = key.split("@").first
|
|
72
74
|
Array(entries).filter_map { |e| e["installPath"] if e.is_a?(Hash) }.uniq.flat_map do |root|
|
|
73
75
|
from_dir(root, prefix: plugin)
|
|
@@ -10,15 +10,27 @@ module ClaudeInbox
|
|
|
10
10
|
def success? = status.success?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# `asdf exec`, which the ruby shim runs us through, puts the ruby it
|
|
14
|
+
# picked ahead of every shim on PATH and names it in ASDF_INSTALL_PATH.
|
|
15
|
+
# Left in place, every session runs our ruby whatever its project's
|
|
16
|
+
# .tool-versions says.
|
|
17
|
+
def self.unpinned(env)
|
|
18
|
+
install = env["ASDF_INSTALL_PATH"] or return env
|
|
19
|
+
prefix = File.join(install, "")
|
|
20
|
+
path = env.fetch("PATH", "").split(File::PATH_SEPARATOR).reject { |dir| dir.start_with?(prefix) }
|
|
21
|
+
env.reject { |k, _| k.start_with?("ASDF_INSTALL_") }.merge("PATH" => path.join(File::PATH_SEPARATOR))
|
|
22
|
+
end
|
|
23
|
+
|
|
13
24
|
# Children start in the environment from before Bundler, or every ruby a
|
|
14
|
-
# session runs, in any project, loads our Gemfile
|
|
15
|
-
# than set on ENV, which the poll
|
|
16
|
-
|
|
25
|
+
# session runs, in any project, loads our Gemfile, and from before asdf
|
|
26
|
+
# pinned our ruby. Handed to exec rather than set on ENV, which the poll
|
|
27
|
+
# and background threads share.
|
|
28
|
+
CHILD_ENV = unpinned(defined?(Bundler.unbundled_env) ? Bundler.unbundled_env : ENV.to_h).freeze
|
|
17
29
|
|
|
18
30
|
module_function
|
|
19
31
|
|
|
20
32
|
# argv as exec and Process.spawn take it, with CHILD_ENV in place of ours.
|
|
21
|
-
def command(*argv) =
|
|
33
|
+
def command(*argv) = [CHILD_ENV, *argv, {unsetenv_others: true}]
|
|
22
34
|
|
|
23
35
|
# `input` is written before any output is read, so it is for the few
|
|
24
36
|
# bytes a clipboard takes, not a stream.
|
data/lib/claude_inbox.rb
CHANGED