dev_context 1.2.1 → 2.0.1

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: 5acaef7b6cb13adb292bcdbc2f59bff0a659517d3b4d17f36f23b8afb11ee40d
4
- data.tar.gz: c5d9cb92a6785bc719b810d66ea18badfd201c2c3fdf9ae438a5e4c41fca6994
3
+ metadata.gz: a909bb66276763e6e8cf92b56a703f63a7a81637977dd4e2c2ac6a2495ac6a0f
4
+ data.tar.gz: dbf0c96d8324235871af7ffda020f761287d66c980d7d34ae58082c7018ebffa
5
5
  SHA512:
6
- metadata.gz: e3488671822dfdd73075c1f6c891ec961e62815807313b1fec8d229a70a3fd8867378b7c0089f4e697e3ed17e5fe3e2b0c19bdaea00d214c7df5817a3506f07a
7
- data.tar.gz: aa423b93528d9f84023bf40707739cc730c73eecf0a0e0ef45277ca68834a301e25a03195d8a8cd9f7b0da0bd8eff55012b8eefc5730b4f35c90d9456e3a18a4
6
+ metadata.gz: d6169e888f0c0d6ec0ad01828ccbf24f1e961616f7ff01d044c63b9bef40cccc03b131a6b77ddae3ad05161c0a6a105545d555ceeec3659c1c3ec43bbcbdb903
7
+ data.tar.gz: 45842d4f6b81a31a2323683313bf11772318e62c45a2ef5fa9df4fa80809cc8e83dfe3f167b4bfe036ae8fe48a6bd7768d89284206698bdd2d767de703055558
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ### Monorepo tooling (root-level)
4
+
5
+ - Add `CLAUDE.md` with codebase guidance for Claude Code.
6
+ - Add root `Gemfile` proxying to `implementations/ruby/` via `eval_gemfile` so `bundle install` works from repo root.
7
+ - Add root `.rspec` and `spec/` symlinks so `bundle exec rspec` works from repo root without `cd`.
8
+ - Add `ruby-bundle-ready` Makefile prerequisite so `test-ruby`, `install-ruby`, and `release-ruby` ensure Bundler state before running.
9
+
10
+ ## [2.0.1] - 2026-06-25
11
+
12
+ - cleaned up erb and updated psych gems to avoid ARM crash on an older version of psych.
13
+
14
+ ## [2.0.0] - 2026-06-23
15
+
16
+ ### Added
17
+
18
+ - Add `dx import` to ingest shell dirstack into `dx` active stack and resynchronize shell stack via eval script.
19
+ - Add optional auto-reconcile gate via `DX_AUTO_IMPORT_SHELL_STACK=true` for stateful commands (`activate|cd`, `pushd`, `popd`).
20
+ - Add shell wrapper transport of live dirstack via `DX_SHELL_STACK` for wrapped stack-aware commands.
21
+ - Add end-to-end coverage in `bash` and `zsh` for mixed shell/dx dirstack mutations and reconciliation.
22
+
23
+ ### Changed
24
+
25
+ - Strengthen `dx`/shell stack interoperability so manual shell `pushd`/`popd` changes can be reconciled by `dx import`.
26
+ - Extend wrapped command handling in shell integration to include `import`.
27
+
28
+ ### Breaking Changes
29
+
30
+ - Shell integration semantics now include dirstack payload transport (`DX_SHELL_STACK`) and `import` in wrapped eval paths.
31
+ - Users relying on previous loose coupling between shell dirstack and `dx` active stack should treat this release as a behavior-boundary change.
32
+
33
+ ### Migration Notes
34
+
35
+ - Run `dx init` and re-source `~/.dx.sh` after upgrading.
36
+ - Use `dx import` to reconcile shell stack changes into `dx` state on demand.
37
+ - Enable `DX_AUTO_IMPORT_SHELL_STACK=true` only if you want reconciliation to happen automatically before stateful `dx` commands.
38
+
3
39
  ## [1.1.0] - 2026-05-15
4
40
 
5
41
  - Add `dx doctor` diagnostics command with `dx check` alias.
data/README.md CHANGED
@@ -14,7 +14,8 @@ A context is one `(repo_dir, branch)` pair. Context names can be explicit, or im
14
14
  Install the gem in the current ruby gem path, with executables installed as:
15
15
 
16
16
  - `~/bin/dev_context` (launcher, honors `DX_IMPL`)
17
- - `~/bin/dev_context-ruby` (Ruby implementation entrypoint)
17
+ - `~/bin/dev_context-rb` (Ruby implementation entrypoint)
18
+ - `~/bin/dev_context-ruby` (legacy Ruby implementation alias)
18
19
 
19
20
  To clone from the github repo:
20
21
 
@@ -41,17 +42,28 @@ To support `cd`, `dx` should be wrapped by a shell function that evaluates shell
41
42
 
42
43
  ```bash
43
44
  dx() {
45
+ local bin
46
+ case "${DX_IMPL:-go}" in
47
+ go)
48
+ bin=dev_context-go ;;
49
+ ruby)
50
+ bin=dev_context-rb ;;
51
+ elixir)
52
+ bin=dev_context-ex ;;
53
+ *)
54
+ echo 1>&2 "No idea what $DX_IMPL means!" ; exit 1 ;;
55
+ esac
44
56
  case "$1" in
45
57
  cd|activate|pushd|popd|pu|po)
46
58
  local out
47
- out="$(DX_SHELL_WRAPPED=1 command dev_context "$@")" || return $?
59
+ out="$(DX_SHELL_WRAPPED=1 command $bin "$@")" || return $?
48
60
  case "$out" in
49
61
  "# DX_SHELL_EVAL"*) eval "$out" ;;
50
62
  *) printf "%s\n" "$out" ;;
51
63
  esac
52
64
  ;;
53
65
  *)
54
- command dev_context "$@"
66
+ command $bin "$@"
55
67
  ;;
56
68
  esac
57
69
  }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/dev_context"
5
+
6
+ exit DevContext::CLI.run(ARGV)
@@ -64,6 +64,8 @@ module DevContext
64
64
  return cmd_help_topic(command)
65
65
  end
66
66
 
67
+ maybe_auto_import_shell_stack!(command)
68
+
67
69
  case command
68
70
  when "version" then cmd_version
69
71
  when "init" then cmd_init
@@ -76,6 +78,8 @@ module DevContext
76
78
  when "deactivate" then cmd_deactivate
77
79
  when "doctor", "check" then cmd_doctor
78
80
  when "find" then cmd_find
81
+ when "import" then cmd_import
82
+ when "stash" then cmd_stash
79
83
  when "stashes" then cmd_stashes
80
84
  when "repos" then cmd_repos
81
85
  when "status", "wip" then cmd_status(mode: command)
@@ -97,9 +101,11 @@ module DevContext
97
101
  attr_reader :argv, :out, :err, :env, :pwd, :stdin, :config, :matcher
98
102
 
99
103
  COMMANDS = %w[
100
- version init help add clone create remove active deactivate doctor check find repos stashes status wip diff branches br co checkout cd activate pushd popd
104
+ version init help add clone create remove active deactivate doctor check find repos stash stashes status wip diff branches br co checkout cd activate pushd popd import
101
105
  ].freeze
102
106
 
107
+ STATEFUL_COMMANDS = %w[activate cd pushd popd].freeze
108
+
103
109
  def default_action
104
110
  return cmd_init unless config.initialized?
105
111
 
@@ -110,5 +116,13 @@ module DevContext
110
116
  out.puts("dx #{DevContext::VERSION} (impl: ruby)")
111
117
  0
112
118
  end
119
+
120
+ def maybe_auto_import_shell_stack!(command)
121
+ return unless STATEFUL_COMMANDS.include?(command)
122
+ return unless truthy?(env.fetch("DX_AUTO_IMPORT_SHELL_STACK", "false"))
123
+ return unless shell_stack_imbalanced?
124
+
125
+ cmd_import(emit_script: false)
126
+ end
113
127
  end
114
128
  end
@@ -70,7 +70,8 @@ module DevContext
70
70
  script = ShellEmitter.new(
71
71
  context: context,
72
72
  remote_name: env.fetch("DX_GIT_REMOTE_NAME", "USE-REPO"),
73
- auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true"))
73
+ auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true")),
74
+ stack_paths: config.active_contexts.map { |ctx| ctx.fetch("repo_path") }
74
75
  ).activation_script
75
76
  out.write(script)
76
77
  0
@@ -39,7 +39,6 @@ module DevContext
39
39
  content = File.read(shell_path)
40
40
  if content.include?(ShellSetup::MANAGED_MARKER)
41
41
  checks << ok_check("shell-script", "Found managed shell integration at #{shell_path}")
42
- checks << ok_check("shell-managed", "Shell integration is managed by dx init")
43
42
  shell_version = extract_shell_script_version(content)
44
43
  if shell_version.nil?
45
44
  checks << warn_check("shell-version", "Managed shell integration is missing version marker")
@@ -93,6 +92,8 @@ module DevContext
93
92
  checks = []
94
93
  if missing.empty?
95
94
  checks << ok_check("repo-paths", "All known repo paths exist")
95
+ elsif fixture_mode?
96
+ checks << warn_check("repo-paths", "Fixture mode enabled; ignoring missing repo paths: #{missing.join(', ')}")
96
97
  else
97
98
  checks << fail_check("repo-paths", "Missing repo paths: #{missing.join(', ')}")
98
99
  end
@@ -162,6 +163,11 @@ module DevContext
162
163
  rescue StandardError
163
164
  false
164
165
  end
166
+
167
+ def fixture_mode?
168
+ value = env.fetch("DX_DOCTOR_FIXTURE_MODE", "")
169
+ %w[1 true yes on].include?(value.to_s.strip.downcase)
170
+ end
165
171
  end
166
172
  end
167
173
  end
@@ -3,6 +3,17 @@
3
3
  module DevContext
4
4
  module Commands
5
5
  module GitOps
6
+ def cmd_import(emit_script: true)
7
+ imported_paths = parsed_shell_stack_paths
8
+ imported_contexts = import_contexts_for_paths(imported_paths)
9
+ config.active_stack.replace(imported_contexts.map { |ctx| ctx.fetch("name") })
10
+ config.send(:save!)
11
+ return 0 unless emit_script
12
+
13
+ out.write(stack_sync_script_for_active_contexts)
14
+ 0
15
+ end
16
+
6
17
  def cmd_active
7
18
  active = config.active_contexts
8
19
  if active.empty?
@@ -46,7 +57,8 @@ module DevContext
46
57
  script = ShellEmitter.new(
47
58
  context: context,
48
59
  remote_name: env.fetch("DX_GIT_REMOTE_NAME", "USE-REPO"),
49
- auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true"))
60
+ auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true")),
61
+ stack_paths: config.active_contexts.map { |ctx| ctx.fetch("repo_path") }
50
62
  ).activation_script
51
63
  out.write(script)
52
64
  0
@@ -65,7 +77,7 @@ module DevContext
65
77
  removed = config.deactivate_context!(context.fetch("name"))
66
78
  return not_found(target) unless removed
67
79
 
68
- out.puts("Popped #{context.fetch('name')}")
80
+ print_pop_message("Popped #{context.fetch('name')}")
69
81
  else
70
82
  top_name = config.active_stack.first
71
83
  if top_name.nil?
@@ -75,13 +87,10 @@ module DevContext
75
87
 
76
88
  context = config.contexts.fetch(top_name)
77
89
  config.deactivate_context!(top_name)
78
- out.puts("Popped #{context.fetch('name')}")
90
+ print_pop_message("Popped #{context.fetch('name')}")
79
91
  end
80
92
 
81
- new_top = config.active_contexts.first
82
- return 0 unless new_top
83
-
84
- out.puts("cd #{Shellwords.escape(new_top.fetch('repo_path'))}")
93
+ out.write(stack_sync_script_for_active_contexts)
85
94
  0
86
95
  end
87
96
 
@@ -187,6 +196,56 @@ module DevContext
187
196
 
188
197
  private
189
198
 
199
+ def parsed_shell_stack_paths
200
+ seen = {}
201
+ env.fetch("DX_SHELL_STACK", "")
202
+ .lines
203
+ .map(&:strip)
204
+ .reject(&:empty?)
205
+ .map { |path| File.expand_path(path, pwd) }
206
+ .select { |path| git_repo?(path) }
207
+ .reject do |path|
208
+ already_seen = seen[path]
209
+ seen[path] = true
210
+ already_seen
211
+ end
212
+ end
213
+
214
+ def import_contexts_for_paths(paths)
215
+ paths.map do |repo_path|
216
+ branch = current_branch(repo_path) || "main"
217
+ context = config.contexts.values.find { |ctx| ctx["repo_path"] == repo_path && ctx["branch"] == branch } ||
218
+ config.contexts.values.find { |ctx| ctx["repo_path"] == repo_path }
219
+ unless context
220
+ name = implicit_context_name(repo_path, branch)
221
+ context_name = ensure_unique_import_context_name(name, repo_path, branch)
222
+ config.add_context(name: context_name, repo_path: repo_path, branch: branch)
223
+ context = config.get_context(context_name)
224
+ end
225
+ context
226
+ end.compact
227
+ end
228
+
229
+ def ensure_unique_import_context_name(base_name, repo_path, branch)
230
+ existing = config.contexts[base_name]
231
+ return base_name unless existing
232
+ return base_name if existing["repo_path"] == repo_path && existing["branch"] == branch
233
+
234
+ suffix = 2
235
+ loop do
236
+ candidate = "#{base_name}-#{suffix}"
237
+ match = config.contexts[candidate]
238
+ return candidate unless match
239
+ return candidate if match["repo_path"] == repo_path && match["branch"] == branch
240
+
241
+ suffix += 1
242
+ end
243
+ end
244
+
245
+ def shell_stack_imbalanced?
246
+ parsed_shell_stack_paths != config.active_contexts.map { |ctx| File.expand_path(ctx.fetch("repo_path")) }
247
+ end
248
+
190
249
  def rotate_active_stack_to(token)
191
250
  stack = config.active_stack
192
251
  return no_active_contexts if stack.empty?
@@ -204,11 +263,13 @@ module DevContext
204
263
  return 1
205
264
  end
206
265
 
207
- config.activate_context!(context_name)
266
+ config.active_stack.replace(stack.rotate(idx))
267
+ config.send(:save!)
208
268
  script = ShellEmitter.new(
209
269
  context: context,
210
270
  remote_name: env.fetch("DX_GIT_REMOTE_NAME", "USE-REPO"),
211
- auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true"))
271
+ auto_create_local_branch: truthy?(env.fetch("DX_AUTO_CREATE_LOCAL_BRANCH", "true")),
272
+ stack_paths: config.active_contexts.map { |ctx| ctx.fetch("repo_path") }
212
273
  ).activation_script
213
274
  out.write(script)
214
275
  0
@@ -227,12 +288,8 @@ module DevContext
227
288
  context_name = stack.delete_at(idx)
228
289
  context = config.contexts[context_name]
229
290
  config.send(:save!)
230
- out.puts("Popped #{context ? context.fetch('name') : context_name}")
231
-
232
- new_top = config.active_contexts.first
233
- return 0 unless new_top
234
-
235
- out.puts("cd #{Shellwords.escape(new_top.fetch('repo_path'))}")
291
+ print_pop_message("Popped #{context ? context.fetch('name') : context_name}")
292
+ out.write(stack_sync_script_for_active_contexts)
236
293
  0
237
294
  end
238
295
 
@@ -250,6 +307,28 @@ module DevContext
250
307
  out.puts("No active contexts")
251
308
  0
252
309
  end
310
+
311
+ def stack_sync_script_for_active_contexts
312
+ paths = config.active_contexts.map { |ctx| File.expand_path(ctx.fetch("repo_path")) }
313
+ script = +"#{ShellEmitter::SCRIPT_MARKER}\n"
314
+ script << "dirs -c >/dev/null 2>&1 || true\n"
315
+ return script if paths.empty?
316
+
317
+ script << "cd #{Shellwords.escape(paths.first)}\n"
318
+ paths.drop(1).each do |path|
319
+ script << "pushd #{Shellwords.escape(path)} >/dev/null 2>&1\n"
320
+ script << "pushd +1 >/dev/null 2>&1\n"
321
+ end
322
+ script
323
+ end
324
+
325
+ def print_pop_message(message)
326
+ if env["DX_SHELL_WRAPPED"] == "1"
327
+ err.puts(message)
328
+ else
329
+ out.puts(message)
330
+ end
331
+ end
253
332
  end
254
333
  end
255
334
  end
@@ -33,11 +33,13 @@ module DevContext
33
33
  dx <pattern> # shorthand for: dx find <pattern>
34
34
  dx help
35
35
  dx init [ruby|go|elixir]
36
+ dx import
36
37
  dx version
37
38
  dx popd [CONTEXT|+N|-N]
38
39
  dx pushd [CONTEXT|PATH|URL|+N|-N]
39
40
  dx repos [PATTERN]
40
41
  dx remove CONTEXT|PATH
42
+ dx stash [--list] [--all|-a] [PATTERN]
41
43
  dx stashes [--list] [PATTERN]
42
44
  dx status|wip
43
45
  HELP
@@ -50,6 +52,8 @@ module DevContext
50
52
  "Usage: dx status [--all] [--dirty] [-b BRANCHPATTERN] [-p PATHPATTERN]\n dx wip [--all] [--dirty] [-b BRANCHPATTERN] [-p PATHPATTERN]\n\nShow branch and git status for active contexts. Use --all for all known repos, --dirty to filter non-clean repos, and -b/-p to filter by branch/path."
51
53
  when "repos"
52
54
  "Usage: dx repos [-b BRANCHPATTERN] [-p PATHPATTERN] [PATTERN]\n\nList all known repos without git status details. PATTERN is shorthand for path filtering."
55
+ when "stash"
56
+ "Usage: dx stash [--list] [--all|-a] [PATTERN]\n\nShow stashes for the active top context by default. Use --all/-a to scan all known repos. Provide PATTERN to filter stash titles; use --list to print matching entries."
53
57
  when "stashes"
54
58
  "Usage: dx stashes [--list] [PATTERN]\n\nShow repos with stashes. Provide PATTERN to filter stash titles; use --list to print matching entries."
55
59
  when "find"
@@ -72,6 +76,8 @@ module DevContext
72
76
  "Usage: dx pushd [CONTEXT|PATH|URL|+N|-N]\n\nCONTEXT accepts exact or fuzzy-matched context names. PATH and URL resolve/create contexts. +N/-N select existing active-stack entries (+0 top/left, -0 bottom/right) and rotate that entry to top."
73
77
  when "popd"
74
78
  "Usage: dx popd [CONTEXT|+N|-N]\n\nWith no args, same as `dx popd +0`. CONTEXT accepts exact or fuzzy-matched context names. +N counts from top/left (+0 top), -N from bottom/right (-0 bottom)."
79
+ when "import"
80
+ "Usage: dx import\n\nImport shell dirstack into dx active_stack (deduped by absolute path) and emit shell sync script."
75
81
  when "remove"
76
82
  "Usage: dx remove CONTEXT|PATH"
77
83
  when "diff"
@@ -3,6 +3,41 @@
3
3
  module DevContext
4
4
  module Commands
5
5
  module Stashes
6
+ def cmd_stash
7
+ args = argv.dup
8
+ show_list = false
9
+ all = false
10
+ parser = OptionParser.new
11
+ parser.on("-l", "--list", "Show stash entry titles per repo") { show_list = true }
12
+ parser.on("-a", "--all", "Show stashes across all known repos") { all = true }
13
+ parser.parse!(args)
14
+ pattern = args.shift
15
+ return usage_error("dx stash [--list] [--all|-a] [PATTERN]") unless args.empty?
16
+
17
+ rows = if all
18
+ known_repo_stash_rows(pattern: pattern)
19
+ else
20
+ active_top_stash_rows(pattern: pattern)
21
+ end
22
+ if rows.empty?
23
+ out.puts("No repos with stashes")
24
+ return 0
25
+ end
26
+
27
+ render_stash_table(rows)
28
+ return 0 unless show_list
29
+
30
+ out.puts
31
+ rows.each do |row|
32
+ out.puts("#{display_path(row[:path])}:")
33
+ row[:entries].each { |entry| out.puts(" - #{entry}") }
34
+ end
35
+ 0
36
+ rescue OptionParser::InvalidOption => e
37
+ err.puts("dx: #{e.message}")
38
+ usage_error("dx stash [--list] [--all|-a] [PATTERN]")
39
+ end
40
+
6
41
  def cmd_stashes
7
42
  args = argv.dup
8
43
  show_list = false
@@ -34,6 +69,26 @@ module DevContext
34
69
 
35
70
  private
36
71
 
72
+ def active_top_stash_rows(pattern: nil)
73
+ context = config.active_contexts.first
74
+ return [] unless context
75
+
76
+ path = context["repo_path"]
77
+ expanded = File.expand_path(path)
78
+ return [] unless File.directory?(expanded)
79
+
80
+ entries = stash_entries(expanded)
81
+ entries = entries.select { |entry| pattern_match?(entry, pattern) } if pattern
82
+ return [] if entries.empty?
83
+
84
+ [{
85
+ path: expanded,
86
+ branch: current_branch(expanded) || "-",
87
+ count: entries.length,
88
+ entries: entries
89
+ }]
90
+ end
91
+
37
92
  def render_stash_table(rows)
38
93
  display_rows = rows.map { |row| row.merge(path: display_path(row[:path])) }
39
94
  path_w = [display_rows.map { |r| r[:path].length }.max || 0, "Path".length].max
@@ -6,10 +6,11 @@ module DevContext
6
6
  class ShellEmitter
7
7
  SCRIPT_MARKER = "# DX_SHELL_EVAL".freeze
8
8
 
9
- def initialize(context:, remote_name:, auto_create_local_branch:)
9
+ def initialize(context:, remote_name:, auto_create_local_branch:, stack_paths: nil)
10
10
  @context = context
11
11
  @remote_name = remote_name
12
12
  @auto_create_local_branch = auto_create_local_branch
13
+ @stack_paths = Array(stack_paths).map { |path| File.expand_path(path) }
13
14
  end
14
15
 
15
16
  def activation_script
@@ -51,11 +52,25 @@ module DevContext
51
52
  git remote get-url #{forced_remote} >/dev/null 2>&1 || __dx_fail "dx: remote '#{forced_remote}' does not exist in this repo" 19
52
53
  git pull --ff-only --autostash #{forced_remote} #{branch} || __dx_fail "dx: pull failed from #{forced_remote}/#{branch} (you are already in the target repo for investigation)" 20
53
54
  fi
55
+ #{stack_sync_script}
54
56
  SH
55
57
  end
56
58
 
57
59
  private
58
60
 
59
- attr_reader :context, :remote_name, :auto_create_local_branch
61
+ attr_reader :context, :remote_name, :auto_create_local_branch, :stack_paths
62
+
63
+ def stack_sync_script
64
+ paths = stack_paths
65
+ return "dirs -c >/dev/null 2>&1 || true" if paths.empty?
66
+
67
+ lines = ["dirs -c >/dev/null 2>&1 || true", "cd #{Shellwords.escape(paths.first)} || __dx_fail \"dx: could not cd into #{Shellwords.escape(paths.first)}\" 21"]
68
+ paths.drop(1).each do |path|
69
+ escaped = Shellwords.escape(path)
70
+ lines << "pushd #{escaped} >/dev/null 2>&1 || __dx_fail \"dx: could not pushd #{escaped}\" 22"
71
+ lines << "pushd +1 >/dev/null 2>&1 || __dx_fail \"dx: could not rotate dirstack\" 23"
72
+ end
73
+ lines.join("\n")
74
+ end
60
75
  end
61
76
  end
@@ -46,17 +46,16 @@ module DevContext
46
46
  go)
47
47
  bin=dev_context-go ;;
48
48
  ruby)
49
- bin=dev_context.rb ;;
49
+ bin=dev_context-rb ;;
50
50
  elixir)
51
51
  bin=dev_context-ex ;;
52
52
  *)
53
53
  echo 1>&2 "No idea what $DX_IMPL means!" ; exit 1 ;;
54
54
  esac
55
- local out
56
55
  case "$1" in
57
- cd|activate|pushd|popd)
56
+ cd|activate|pushd|popd|pu|po|import)
58
57
  local out
59
- out="$(DX_SHELL_WRAPPED=1 command $bin "$@")" || return $?
58
+ out="$(DX_SHELL_WRAPPED=1 DX_SHELL_STACK="$(dirs -p 2>/dev/null || pwd)" command $bin "$@")" || return $?
60
59
  case "$out" in
61
60
  "# DX_SHELL_EVAL"*) eval "$out" ;;
62
61
  *) printf "%s\\n" "$out" ;;
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DevContext
4
- SHELL_VERSION = "1"
4
+ SHELL_VERSION = "2"
5
5
  RELEASES = [
6
- ["1.0.1", '2026-05-12'],
6
+ ["2.0.1", '2026-06-25'],
7
+ ["2.0.0", '2026-06-23'],
8
+ ["1.2.0", '2026-05-28'],
7
9
  ["1.1.0", '2026-05-15'],
8
- ["1.2.1", '2026-05-26']
10
+ ["1.0.1", '2026-05-12']
9
11
  ].freeze
10
- VERSION = RELEASES.last.first
12
+ VERSION = RELEASES[0][0]
11
13
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dev_context
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alan Stebbens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-26 00:00:00.000000000 Z
11
+ date: 2026-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: psych
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.4'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: fuubar
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,6 +74,7 @@ email:
60
74
  - aks@stebbens.org
61
75
  executables:
62
76
  - dev_context
77
+ - dev_context-rb
63
78
  - dev_context-ruby
64
79
  extensions: []
65
80
  extra_rdoc_files: []
@@ -71,6 +86,7 @@ files:
71
86
  - README.md
72
87
  - bin/console
73
88
  - bin/dev_context
89
+ - bin/dev_context-rb
74
90
  - bin/dev_context-ruby
75
91
  - bin/setup
76
92
  - lib/dev_context.rb