dev_context 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3ec8da4562204b25b61b33aa7c0bcf48e75c4db79d85ad153f59c86dffad62a1
4
+ data.tar.gz: 5c7a271e4eabb39c9e60d009c0da8a983b4d2e907029c32b095e46cda997b60b
5
+ SHA512:
6
+ metadata.gz: b8c424382c0f58b5be6254621cec4cc6b0a204a5f934417fdaec0f5f4a1f98da35877709d54c278116bf7b3ed394dfd616d2721cfe6b3fa884ca55214aeebe16
7
+ data.tar.gz: 0e9458aa53afb5729b9270e56b90ff0e1be01ef3e56a00818a0843e6a0206949672f3c69edc6381b0163ab74d7a711871c9b09d19a38f7a9c7a79e68eeb4e4a3
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-05-01
4
+
5
+ - Initial release
@@ -0,0 +1,18 @@
1
+ # Code of Conduct
2
+
3
+ This repository, "dev_context", follows [The Ruby Community Conduct
4
+ Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space",
5
+ which is defined as community communications channels (such as mailing lists,
6
+ submitted patches, commit comments, etc.):
7
+
8
+ * Participants will be tolerant of opposing views.
9
+
10
+ * Participants must ensure that their language and actions are free of personal
11
+ * attacks and disparaging personal remarks.
12
+
13
+ * When interpreting the words and actions of others, participants should always
14
+ * assume good intentions.
15
+
16
+ * Behaviour which can be reasonably considered harassment will not be tolerated.
17
+
18
+ If you have any concerns about behaviour within this project, please contact us at ["aks@stebbens.org"](mailto:"aks@stebbens.org").
data/DESIGN.md ADDED
@@ -0,0 +1,250 @@
1
+ # dx — DevContext: Design
2
+
3
+ `dx` is a Ruby CLI for managing development context across repositories and branches.
4
+ It is the layer between filesystem navigation, Git branch intent, and daily workflow.
5
+
6
+ ---
7
+
8
+ ## 1) Core Model
9
+
10
+ - **Context identity:** a context is exactly one pair: `(repo_dir, branch)`.
11
+ - **Context naming:** a context name is either:
12
+ - explicit (provided by user), or
13
+ - implicit: `<repo_basename>:<branch>`.
14
+ - **Name normalization:** all context names are stored lowercase; lookup is case-insensitive.
15
+ - **Active stack:** `dx` maintains an ordered stack of active contexts, most recent first.
16
+ - **Scope for status/diff/branches/wip:** operates on active contexts.
17
+
18
+ ---
19
+
20
+ ## 2) Shell Integration Model
21
+
22
+ Because a child process cannot change the parent shell CWD, `dx` is used through a shell function wrapper.
23
+
24
+ - The wrapper calls the Ruby executable and evaluates emitted shell for stateful commands.
25
+ - The Ruby executable supports:
26
+ - **shell-emitting mode** for commands like `cd`, `activate`, `pushd`, and `popd`
27
+ - **plain output mode** for report commands like `status`, `diff`, `branches`, `wip`
28
+ - Shell-emitting responses are prefixed with a marker (`# DX_SHELL_EVAL`) so wrappers can detect eval-safe output without hardcoding command names.
29
+
30
+ ### Required behavior
31
+
32
+ - `dx cd ...` changes the current shell directory.
33
+ - `dx activate ...` also changes directory (same destination semantics as `dx cd`).
34
+ - `dx pushd ...` changes current shell directory and pushes context on active stack.
35
+ - `dx popd ...` removes context from active stack and changes directory to new top context when present.
36
+ - On partial failure (for example pull conflict), CWD remains in the target repo to enable direct investigation.
37
+
38
+ ---
39
+
40
+ ## 3) Activation Semantics
41
+
42
+ Activating a context should be DWIM and deterministic:
43
+
44
+ 1. Resolve the requested context (exact name, exact path, FSF fuzzy).
45
+ 2. Ensure repository exists and is a valid Git repo.
46
+ 3. Resolve/switch branch:
47
+ - if branch exists locally: checkout it
48
+ - else if remote branch exists and `DX_AUTO_CREATE_LOCAL_BRANCH=true`: create tracking branch
49
+ - else fail with actionable guidance
50
+ 4. Pull latest changes using configured remote policy.
51
+ 5. Persist activation metadata (`last_used_at`, active stack update).
52
+
53
+ ### Pull behavior UX
54
+
55
+ - Use safe defaults (fast-forward only).
56
+ - If pull fails:
57
+ - keep user in target repo/branch
58
+ - return non-zero
59
+ - print short next-step hints (`git status`, conflict resolution, retry strategy)
60
+
61
+ ---
62
+
63
+ ## 4) Remote Resolution Policy
64
+
65
+ - `DX_GIT_REMOTE_NAME=USE-REPO` by default.
66
+ - If `USE-REPO`, resolve remote from the repository itself:
67
+ - prefer branch upstream (`@{upstream}`)
68
+ - otherwise use single-remote fallback when unambiguous
69
+ - fail with guidance when ambiguous or absent
70
+ - If `DX_GIT_REMOTE_NAME=<name>` (for example `origin`), force that remote.
71
+
72
+ `~/.dxcf` does not duplicate Git remote topology; Git remains source of truth.
73
+
74
+ ---
75
+
76
+ ## 5) Global Config: `~/.dxcf`
77
+
78
+ Store as YAML. Suggested v1 shape:
79
+
80
+ ```yaml
81
+ version: 1
82
+
83
+ contexts:
84
+ dev_context:main:
85
+ name: dev_context:main
86
+ repo_path: /Users/aks/src/github/aks/dev_context
87
+ branch: main
88
+ created_at: 2026-05-12T09:30:00Z
89
+ last_used_at: 2026-05-12T10:15:00Z
90
+
91
+ active_stack:
92
+ - dev_context:main
93
+
94
+ repos:
95
+ dev_context:
96
+ path: /Users/aks/src/github/aks/dev_context
97
+ last_seen_at: 2026-05-12T10:15:00Z
98
+
99
+ clone_roots:
100
+ - /Users/aks/src/github
101
+ ```
102
+
103
+ Notes:
104
+ - context key collisions are prevented via normalized lowercase naming
105
+ - repo metadata and context metadata are separate concerns
106
+
107
+ ---
108
+
109
+ ## 6) Name Resolution and Matching
110
+
111
+ Resolution order for context arguments:
112
+
113
+ 1. exact context name match
114
+ 2. exact path match
115
+ 3. `DX_PATH` + `DX_CLONE_BASE_DIR` directory search for relative repo targets
116
+ 4. FSF-style fuzzy match against known context names and repo basenames
117
+
118
+ If fuzzy matching returns multiple candidates, `dx` lists them and exits non-zero.
119
+
120
+ ---
121
+
122
+ ## 7) URL and Path Handling
123
+
124
+ Accepted location inputs:
125
+
126
+ - relative path (`./api`)
127
+ - absolute path (`/Users/aks/src/github/api`)
128
+ - Git URL (`https://github.com/org/repo.git`, `git@github.com:org/repo.git`)
129
+
130
+ `DX_PATH` behavior for repo target lookup:
131
+
132
+ - `DX_PATH` is a path-separated list of directories (like `PATH`).
133
+ - When a relative target is not found under current working directory, `dx` searches each `DX_PATH` root and `DX_CLONE_BASE_DIR` for `<root>/<target>`.
134
+ - A single match is accepted.
135
+ - Multiple matches are treated as ambiguous and fail with candidate listing.
136
+ - If exact path search does not find a unique match, `dx` performs FSF subsequence matching across directory basenames in those roots.
137
+
138
+ URL behavior:
139
+
140
+ 1. derive repo basename (`repo.git` -> `repo`)
141
+ 2. place clone under `clone_roots`/`DX_CLONE_BASE_DIR` policy
142
+ 3. clone if missing, otherwise reuse existing path
143
+ 4. register repo and optionally create context
144
+ 5. activate/cd when command requires it
145
+
146
+ ---
147
+
148
+ ## 8) Command Set (v1)
149
+
150
+ - `dx init`
151
+ - `dx add PATH|URL ...`
152
+ - `dx create NAME [PATH] [BRANCH]`
153
+ - `dx cd CONTEXT|PATH|URL`
154
+ - `dx activate CONTEXT|PATH|URL`
155
+ - `dx pushd CONTEXT|PATH|URL`
156
+ - `dx popd [CONTEXT]`
157
+ - `dx deactivate CONTEXT`
158
+ - `dx active`
159
+ - `dx status` (`dx wip` alias allowed)
160
+ - `dx diff [CONTEXT]`
161
+ - `dx branches [CONTEXT]`
162
+ - `dx co -b FEATURE [CONTEXT]`
163
+ - `dx help [COMMAND]`
164
+ - `dx doctor` (or `dx check`)
165
+
166
+ ---
167
+
168
+ ## 9) Status Output Contract
169
+
170
+ Single-line status summary per active context:
171
+
172
+ - `m`: staged modified count
173
+ - `u`: unstaged modified count
174
+ - `n`: new/untracked count
175
+ - `d`: deleted count
176
+ - `r`: renamed count
177
+
178
+ Example:
179
+
180
+ `m:0 u:10 n:26 d:3 r:0`
181
+
182
+ If all counts are zero, display `Up to date`.
183
+
184
+ ---
185
+
186
+ ## 10) High-Level Ruby Architecture
187
+
188
+ - `DevContext::CLI` - argument parsing and command dispatch
189
+ - `DevContext::Config` - load/save/validate `~/.dxcf`
190
+ - `DevContext::Context` - context value object `(repo_path, branch, name)`
191
+ - `DevContext::Registry` - known repos and contexts
192
+ - `DevContext::Matcher` - exact/fuzzy resolution
193
+ - `DevContext::Git` - status, branch, pull, checkout helpers
194
+ - `DevContext::ShellEmitter` - emits safe shell snippets for stateful commands
195
+ - `DevContext::Commands::*` - command handlers
196
+ - `DevContext::JJ` - optional read-only integration
197
+ - `DevContext::TUI` - optional dashboard (future)
198
+
199
+ Execution flow:
200
+
201
+ 1. Parse command
202
+ 2. Load config
203
+ 3. Resolve target (name/path/url)
204
+ 4. Perform command logic (Git + config updates)
205
+ 5. Emit shell script or plain output
206
+ 6. Return stable exit code
207
+
208
+ ---
209
+
210
+ ## 11) DWIM UX Contract
211
+
212
+ `dx` is a DWIM-first CLI: easy, intuitive, forgiving, and discoverable.
213
+
214
+ - **Default usefulness**
215
+ - Bare `dx` performs the most useful safe default for initialized users (`dx status`).
216
+ - If uninitialized, `dx` guides user to `dx init`.
217
+
218
+ - **Help discoverability**
219
+ - `dx help`
220
+ - `dx help <command>`
221
+ - `dx <command> --help`
222
+ - All three forms are supported and consistent.
223
+
224
+ - **Name resolution**
225
+ - Context lookup order: exact name -> exact path -> FSF fuzzy match.
226
+ - Matching is case-insensitive; stored context names are lowercase.
227
+ - Ambiguous fuzzy matches never guess silently; show candidates and exit non-zero.
228
+
229
+ - **Error ergonomics**
230
+ - Every error message includes:
231
+ - what failed
232
+ - why (if known)
233
+ - one concrete next command
234
+ - Prefer short, actionable output over long diagnostics.
235
+
236
+ - **Activation behavior**
237
+ - `dx activate` / `dx cd` prioritize flow:
238
+ - switch to target repo/branch
239
+ - attempt pull using configured policy
240
+ - On pull failure, keep user in target repo for investigation.
241
+ - Return non-zero on failure so scripts can detect problems.
242
+
243
+ - **Safe automation**
244
+ - Non-destructive by default.
245
+ - No silent data-changing operations without explicit user intent.
246
+ - Behavior controlled by env vars with sensible defaults.
247
+
248
+ - **Operator confidence**
249
+ - `dx doctor` (or `dx check`) validates shell integration, config, and git availability.
250
+ - Command output is stable and script-friendly when possible.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Alan Stebbens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # DevContext (dx)
2
+
3
+ This repo contains `dx` (`dev_context`), a script for managing developer context across repository directories and branches.
4
+
5
+ A context is one `(repo_dir, branch)` pair. Context names can be explicit, or implicit as `<repo_basename>:<branch>` (stored lowercase), and resolved with exact then FSF-style fuzzy matching.
6
+
7
+ ## Installation
8
+
9
+ gem install -n ~/bin dev_context
10
+
11
+ Install the gem in the current ruby gem path, with the executable installed as `~/bin/dev_context.rb`.
12
+
13
+ To clone from the github repo:
14
+
15
+ cd ~/src/github # or wherever you keep github repos
16
+ git clone https://github.com/aks/dev_context.git
17
+ cd dev_context
18
+ bundle install
19
+ bundle exec rake install
20
+
21
+ ## Usage
22
+
23
+ ### Current model (authoritative)
24
+
25
+ - A context is exactly one repo+branch pair (not a multi-repo aggregate).
26
+ - `dx cd` and `dx activate` both activate the target context and change CWD via shell integration.
27
+ - Activation attempts branch checkout + pull; on pull failure, CWD remains in the target repo for investigation and the command exits non-zero.
28
+ - Defaults:
29
+ - `DX_AUTO_CREATE_LOCAL_BRANCH=true`
30
+ - `DX_GIT_REMOTE_NAME=USE-REPO` (or explicit like `origin`)
31
+
32
+ ### Shell integration
33
+
34
+ To support `cd`, `dx` should be wrapped by a shell function that evaluates shell output for stateful commands.
35
+
36
+ ```bash
37
+ dx() {
38
+ case "$1" in
39
+ cd|activate|pushd|popd)
40
+ local out
41
+ out="$(DX_SHELL_WRAPPED=1 command dev_context.rb "$@")" || return $?
42
+ case "$out" in
43
+ "# DX_SHELL_EVAL"*) eval "$out" ;;
44
+ *) printf "%s\n" "$out" ;;
45
+ esac
46
+ ;;
47
+ *)
48
+ command dev_context.rb "$@"
49
+ ;;
50
+ esac
51
+ }
52
+ ```
53
+
54
+ In the descriptions below, the following terms are used consistently:
55
+
56
+ - `NAME`: the context NAME (which can match a PATH)
57
+
58
+ - `PATH`: the file system path to a single repository directory.
59
+
60
+ - `URL`: a standard http/https/git scheme URL that leads to a git-based
61
+ repository.
62
+
63
+ - `CONTEXT`: a context name, a `PATH`, or `URL` that identifies one context.
64
+ Implicit naming uses `<repo_basename>:<branch>`.
65
+
66
+ ### Common Options
67
+
68
+ These options are currently supported on `dx add`, `dx clone`, and `dx create`:
69
+
70
+ - `-c, --context NAME`: explicit context name
71
+ - `-n, --norun`: preview actions without changing state
72
+ - `-v, --verbose`: print extra command details
73
+
74
+ Examples:
75
+
76
+ ```bash
77
+ dx add -c api:main ~/src/github/api
78
+ dx clone -n https://github.com/acme/tooling.git
79
+ dx create -v feature:abc-123 ~/src/github/dev_context feature/abc-123
80
+ ```
81
+
82
+ ### Context stack commands
83
+
84
+ - `dx pushd CONTEXT|PATH|URL`: activate and push context to top of stack
85
+ - `dx popd [CONTEXT]`: pop the top context (or a specific one); if a new top exists, switch CWD to it
86
+
87
+ ### `DX_PATH` repo lookup
88
+
89
+ `DX_PATH` can be used as a list of parent directories that contain repositories.
90
+
91
+ - Format: path-separated directories (same separator as shell `PATH`)
92
+ - Example:
93
+
94
+ ```bash
95
+ export DX_PATH="$HOME/src/github:$HOME/src/work"
96
+ dx add dev_context
97
+ ```
98
+
99
+ Lookup behavior for relative repo targets:
100
+
101
+ 1. check current directory-relative path
102
+ 2. check each `DX_PATH` root and `DX_CLONE_BASE_DIR` as `<root>/<target>`
103
+ 3. if needed, run FSF subsequence search across repo directory names in those roots
104
+ 4. if one match is found, use it
105
+ 5. if multiple matches are found, fail and list candidates
106
+
107
+ ### Initialization and Adding Directories
108
+
109
+ dx [init | help]
110
+ dx init
111
+ dx help
112
+
113
+ The first thing to do is to initialize `dx` with one or more repo directories.
114
+ They do not need to be added but, they can be discovered automatically with `dx
115
+ add PATH`.
116
+
117
+ When initialized, a `~/.dxcf` file will exist with the current `dx` settings.
118
+
119
+ When `dx` is invoked without any arguments (or options), and `~/.dxcf` does
120
+ *not* exist, then `dx init` is performed. `dx init` returns exit code 0
121
+ (success) when it actually succeeds in performing the initialization; it returns
122
+ 1 (fail) otherwise.
123
+
124
+ When `dx` is invoked without any arguments or options, and `~/.dxcf`
125
+ *already* exists, `dx` defaults to `dx status`.
126
+
127
+ | dx command | ~/.dxcf? | action |
128
+ | ---------- | -------- | ----------- |
129
+ | `dx` | no | `dx init` |
130
+ | `dx` | yes | `dx status` |
131
+
132
+ > Note: some command examples below were written before the current context model was finalized.
133
+ > The authoritative behavior is defined in the "Current model (authoritative)" section above and in `DESIGN.md`.
134
+
135
+ ### Command reference (v1)
136
+
137
+ ```text
138
+ dx init
139
+ dx add PATH|URL ...
140
+ dx clone URL [PATH]
141
+ dx create NAME [PATH] [BRANCH]
142
+ dx remove CONTEXT|PATH
143
+ dx active
144
+ dx activate CONTEXT|PATH|URL
145
+ dx cd CONTEXT|PATH|URL
146
+ dx deactivate CONTEXT
147
+ dx pushd CONTEXT|PATH|URL
148
+ dx popd [CONTEXT]
149
+ dx status|wip [--all] [--dirty] [-b BRANCHPATTERN] [-p PATHPATTERN]
150
+ dx repos [-b BRANCHPATTERN] [-p PATHPATTERN] [PATTERN]
151
+ dx stashes [--list] [PATTERN]
152
+ dx find [--stashes|--branches|--paths] <pattern>
153
+ dx find --all
154
+ dx branches|br [CONTEXT|PATTERN]
155
+ dx checkout|co [-b] FEATURE [CONTEXT]
156
+ dx diff [CONTEXT]
157
+ ```
158
+
159
+ Notes:
160
+
161
+ - `dx add` accepts one or more `PATH`/`URL` targets; there is no trailing aggregate context argument.
162
+ - `dx add URL` behaves like clone+register for that repo.
163
+ - `dx stashes [PATTERN]`, `dx repos [PATTERN]`, and `dx branches [PATTERN]` are focused find-style shortcuts.
164
+ - `dx <pattern>` is shorthand for `dx find <pattern>` when the first token is not a command.
165
+ - `dx wip` is `dx status --dirty` by default.
166
+ - `dx status`/`dx wip` show stash count as `s:<count>` when non-zero.
167
+
168
+ ## Development
169
+
170
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
171
+
172
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
173
+
174
+ ## Contributing
175
+
176
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aks/dev_context. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/aks/dev_context/blob/main/CODE_OF_CONDUCT.md).
177
+
178
+ ## License
179
+
180
+ MIT License — see [LICENSE.txt](LICENSE.txt).
181
+
182
+ ## Code of Conduct
183
+
184
+ Please adhere to our [code of conduct](https://github.com/aks/dev_context/blob/main/CODE_OF_CONDUCT.md).
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "dev_context"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
@@ -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)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require "fileutils"
5
+ require "pathname"
6
+ require "optparse"
7
+
8
+ module DevContext
9
+ class CLI
10
+ include Commands::Init
11
+ include Commands::Help
12
+ include Commands::Support
13
+ include Commands::ContextLifecycle
14
+ include Commands::GitOps
15
+ include Commands::SearchHelpers
16
+ include Commands::Find
17
+ include Commands::Repos
18
+ include Commands::Stashes
19
+ include Commands::Status
20
+ include Commands::Remove
21
+
22
+ COLOR_RESET = "\e[0m"
23
+ COLOR_ORANGE = "\e[38;5;208m"
24
+ COLOR_CYAN = "\e[36m"
25
+ COLOR_GREEN = "\e[32m"
26
+ COLOR_RED = "\e[31m"
27
+ COLOR_YELLOW = "\e[33m"
28
+ COLOR_MAGENTA = "\e[35m"
29
+
30
+ def self.run(argv, out: $stdout, err: $stderr, env: ENV, pwd: Dir.pwd)
31
+ new(argv, out: out, err: err, env: env, pwd: pwd).run
32
+ end
33
+
34
+ def initialize(argv, out:, err:, env:, pwd:)
35
+ @argv = argv.dup
36
+ @out = out
37
+ @err = err
38
+ @env = env
39
+ @pwd = pwd
40
+ @config = Config.new
41
+ @matcher = Matcher.new(config: @config)
42
+ end
43
+
44
+ def run
45
+ raw_command = argv.shift
46
+
47
+ return default_action if raw_command.nil?
48
+
49
+ resolution = resolve_command(raw_command)
50
+ command = resolution[:command]
51
+ if command.nil?
52
+ if resolution[:error] == :unknown && !raw_command.start_with?("-")
53
+ argv.unshift(raw_command)
54
+ return cmd_find
55
+ end
56
+ return 1
57
+ end
58
+
59
+ if %w[--help -h].include?(argv.first)
60
+ argv.shift
61
+ return cmd_help_topic(command)
62
+ end
63
+
64
+ case command
65
+ when "init" then cmd_init
66
+ when "help", "--help", "-h" then cmd_help
67
+ when "add" then cmd_add
68
+ when "clone" then cmd_clone
69
+ when "create" then cmd_create
70
+ when "remove" then cmd_remove
71
+ when "active" then cmd_active
72
+ when "deactivate" then cmd_deactivate
73
+ when "find" then cmd_find
74
+ when "stashes" then cmd_stashes
75
+ when "repos" then cmd_repos
76
+ when "status", "wip" then cmd_status(mode: command)
77
+ when "diff" then cmd_diff
78
+ when "branches", "br" then cmd_branches
79
+ when "co", "checkout" then cmd_checkout
80
+ when "cd", "activate", "pushd" then cmd_activate
81
+ when "popd" then cmd_pop
82
+ else
83
+ err.puts("dx: unknown command '#{raw_command}'")
84
+ cmd_help
85
+ 1
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ attr_reader :argv, :out, :err, :env, :pwd, :config, :matcher
92
+
93
+ COMMANDS = %w[
94
+ init help add clone create remove active deactivate find repos stashes status wip diff branches br co checkout cd activate pushd popd
95
+ ].freeze
96
+
97
+ def default_action
98
+ return cmd_init unless config.initialized?
99
+
100
+ cmd_status
101
+ end
102
+ end
103
+ end