hiiro 0.1.30 → 0.1.32
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/README.md +4 -23
- data/TODO.md +21 -0
- data/bin/g-pr +1 -1
- data/bin/h-branch +1 -1
- data/bin/{h-subtask → h-osubtask} +2 -2
- data/bin/{h-task → h-otask} +68 -19
- data/bin/h-pr +1 -1
- data/exe/h +1 -1
- data/lib/hiiro/version.rb +1 -1
- data/notes +224 -0
- data/plugins/{task.rb → old_task.rb} +149 -24
- data/plugins/tasks.rb +813 -0
- metadata +8 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 33682128f00683ee714b4bc368dbcc2243544cf3c495063231ec95497ded5bc8
|
|
4
|
+
data.tar.gz: 8bfb329a9ec2f09a96e886c8a3ea3146ccb7a5a728ca5efc7a83509df3d578a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d4ad31db7afe3ca4ee5f7160e5004a086b8a5962908bc346bd30ca9a0c8a4d6948f1c699339d5103a14ea37b101b1beb8a515f17a154ec77e3b7a422cf8211e
|
|
7
|
+
data.tar.gz: b6f2348435f48f1ba9a2ce9b38cd7a6db4d5aefca7c32d8291e96ff6faf589d061f4b2d9838fc9643ff05988dfd9489680dd684e36a478f11203ceb8b40a311d
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@ See [docs/](docs/) for detailed documentation on all subcommands.
|
|
|
19
19
|
gem install hiiro
|
|
20
20
|
|
|
21
21
|
# Install plugins and subcommands
|
|
22
|
-
|
|
22
|
+
h setup
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
This installs:
|
|
@@ -28,26 +28,15 @@ This installs:
|
|
|
28
28
|
|
|
29
29
|
Ensure `~/bin` is in your `$PATH`.
|
|
30
30
|
|
|
31
|
-
### Manual Installation
|
|
32
|
-
|
|
33
|
-
```sh
|
|
34
|
-
# Copy the main script
|
|
35
|
-
cp bin/h ~/bin/h
|
|
36
|
-
chmod +x ~/bin/h
|
|
37
|
-
|
|
38
|
-
# Copy subcommands (optional)
|
|
39
|
-
cp bin/h-* ~/bin/
|
|
40
|
-
|
|
41
|
-
# Copy plugins (optional)
|
|
42
|
-
mkdir -p ~/.config/hiiro/plugins
|
|
43
|
-
cp plugins/*.rb ~/.config/hiiro/plugins/
|
|
44
|
-
```
|
|
45
31
|
|
|
46
32
|
### Dependencies
|
|
47
33
|
|
|
48
34
|
```sh
|
|
49
35
|
# For notify plugin (macOS)
|
|
50
36
|
brew install terminal-notifier
|
|
37
|
+
|
|
38
|
+
# For fuzzy-finder
|
|
39
|
+
brew install sk
|
|
51
40
|
```
|
|
52
41
|
|
|
53
42
|
## Quick Start
|
|
@@ -56,14 +45,6 @@ brew install terminal-notifier
|
|
|
56
45
|
# List available subcommands
|
|
57
46
|
h
|
|
58
47
|
|
|
59
|
-
# Edit the main h script
|
|
60
|
-
h edit
|
|
61
|
-
|
|
62
|
-
# Get paths
|
|
63
|
-
h path # Print current directory
|
|
64
|
-
h ppath # Print project root (git repo root + relative dir)
|
|
65
|
-
h rpath # Print relative directory from git root
|
|
66
|
-
|
|
67
48
|
# Simple test
|
|
68
49
|
h ping
|
|
69
50
|
# => pong
|
data/TODO.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# To do...
|
|
3
|
+
|
|
4
|
+
- [ ] move task/subtask back into a plugin
|
|
5
|
+
- [ ] have it add :task and :subtask subcommands to the instance of Hiiro
|
|
6
|
+
- [ ] they should init another hiiro instance with a `scope` value set
|
|
7
|
+
- [ ] if scope is :task, it looks at the whole world
|
|
8
|
+
- [ ] if the scope is :subtask, it scopes behavior to the current task
|
|
9
|
+
- [ ] redesign the yml structure for managing tasks
|
|
10
|
+
|
|
11
|
+
```yml
|
|
12
|
+
tasks:
|
|
13
|
+
- name: some_name
|
|
14
|
+
parent_task: parent_task_name (if included it's a subtask)
|
|
15
|
+
tree: tree_name
|
|
16
|
+
root: repo_root/tree_name
|
|
17
|
+
```
|
|
18
|
+
- [ ] refactor task plugin to not be procedural AI slop
|
|
19
|
+
- [ ] convert to js and using node
|
|
20
|
+
- [ ] h-config {vim,zsh,tmux}
|
|
21
|
+
- [ ]
|
data/bin/g-pr
CHANGED
data/bin/h-branch
CHANGED
|
@@ -8,7 +8,7 @@ require "yaml"
|
|
|
8
8
|
require "time"
|
|
9
9
|
|
|
10
10
|
Hiiro.load_env
|
|
11
|
-
hiiro = Hiiro.init(*ARGV, plugins: [Tmux,
|
|
11
|
+
hiiro = Hiiro.init(*ARGV, plugins: [Tmux, OldTask])
|
|
12
12
|
|
|
13
13
|
tasks = hiiro.task_manager
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@ hiiro.add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
|
|
|
16
16
|
hiiro.add_subcmd(:list) { tasks.list_subtasks }
|
|
17
17
|
hiiro.add_subcmd(:ls) { tasks.list_subtasks }
|
|
18
18
|
hiiro.add_subcmd(:new) { |subtask_name| tasks.new_subtask(subtask_name) }
|
|
19
|
-
hiiro.add_subcmd(:switch) { |subtask_name| tasks.switch_subtask(subtask_name) }
|
|
19
|
+
hiiro.add_subcmd(:switch) { |subtask_name=nil| tasks.switch_subtask(subtask_name) }
|
|
20
20
|
hiiro.add_subcmd(:app) { |app_name| tasks.subtask_open_app(app_name) }
|
|
21
21
|
hiiro.add_subcmd(:path) { |app_name=nil| tasks.subtask_app_path(app_name) }
|
|
22
22
|
hiiro.add_subcmd(:cd) { |*args| tasks.subtask_cd_app(*args) }
|
data/bin/{h-task → h-otask}
RENAMED
|
@@ -44,23 +44,55 @@ class TaskManager
|
|
|
44
44
|
active, available = trees.partition { |tree_name| task_for_tree(tree_name) }
|
|
45
45
|
sessions = tmux_sessions
|
|
46
46
|
|
|
47
|
+
# Group active trees by parent task
|
|
48
|
+
groups = {}
|
|
47
49
|
active.each do |tree_name|
|
|
48
50
|
task = task_for_tree(tree_name)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
parent = task.include?('/') ? task.split('/').first : task
|
|
52
|
+
groups[parent] ||= []
|
|
53
|
+
groups[parent] << { tree: tree_name, task: task }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
first_group = true
|
|
57
|
+
groups.each do |parent, entries|
|
|
58
|
+
puts unless first_group
|
|
59
|
+
first_group = false
|
|
60
|
+
|
|
61
|
+
# Sort so the main entry (parent itself or parent/main) comes first
|
|
62
|
+
entries.sort_by! { |e| e[:task] == parent || e[:task].end_with?('/main') ? 0 : 1 }
|
|
63
|
+
|
|
64
|
+
entries.each_with_index do |entry, i|
|
|
65
|
+
tree_name = entry[:tree]
|
|
66
|
+
task = entry[:task]
|
|
67
|
+
marker = (current && current[:tree] == tree_name) ? "*" : " "
|
|
68
|
+
branch = worktree_branch(tree_name)
|
|
69
|
+
branch_str = branch ? " [#{branch}]" : ""
|
|
70
|
+
|
|
71
|
+
# Check if there's a tmux session for this task
|
|
72
|
+
session_name = session_name_for(task)
|
|
73
|
+
has_session = sessions.include?(session_name)
|
|
74
|
+
session_marker = has_session ? "+" : " "
|
|
75
|
+
|
|
76
|
+
if i == 0
|
|
77
|
+
# Parent task line
|
|
78
|
+
display_name = parent
|
|
79
|
+
puts format("%s%s %s%s", marker, session_marker, display_name, branch_str)
|
|
80
|
+
else
|
|
81
|
+
# Subtask line: align /child_name under the parent name
|
|
82
|
+
child_name = task.include?('/') ? task.split('/', 2).last : task
|
|
83
|
+
padding = " " * parent.length
|
|
84
|
+
puts format("%s%s %s/%s%s", marker, session_marker, padding, child_name, branch_str)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
58
87
|
end
|
|
59
88
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
89
|
+
if available.any?
|
|
90
|
+
puts
|
|
91
|
+
available.each do |tree_name|
|
|
92
|
+
branch = worktree_branch(tree_name)
|
|
93
|
+
branch_str = branch ? " [#{branch}]" : ""
|
|
94
|
+
puts format(" %-20s (available)%s", tree_name, branch_str)
|
|
95
|
+
end
|
|
64
96
|
end
|
|
65
97
|
|
|
66
98
|
# List tmux sessions without associated tasks
|
|
@@ -415,11 +447,11 @@ class TaskManager
|
|
|
415
447
|
worktree_info.keys.sort
|
|
416
448
|
end
|
|
417
449
|
|
|
418
|
-
# Parse git worktree list output into { name => path } hash
|
|
419
|
-
def
|
|
420
|
-
@
|
|
450
|
+
# Parse git worktree list output into { name => { path:, branch: } } hash
|
|
451
|
+
def worktree_details
|
|
452
|
+
@worktree_details ||= begin
|
|
421
453
|
output = `git -C #{main_repo_path} worktree list --porcelain 2>/dev/null`
|
|
422
|
-
|
|
454
|
+
details = {}
|
|
423
455
|
current_path = nil
|
|
424
456
|
|
|
425
457
|
work_dir = File.join(Dir.home, 'work')
|
|
@@ -433,6 +465,12 @@ class TaskManager
|
|
|
433
465
|
# Skip bare repo
|
|
434
466
|
current_path = nil
|
|
435
467
|
elsif line.start_with?('branch ') || line == 'detached'
|
|
468
|
+
branch = if line.start_with?('branch ')
|
|
469
|
+
line.sub('branch refs/heads/', '')
|
|
470
|
+
else
|
|
471
|
+
'(detached)'
|
|
472
|
+
end
|
|
473
|
+
|
|
436
474
|
# Capture worktree (both named branches and detached HEAD)
|
|
437
475
|
if current_path && current_path != main_repo_path
|
|
438
476
|
# Use relative path from ~/work/ to support nested worktrees
|
|
@@ -442,17 +480,28 @@ class TaskManager
|
|
|
442
480
|
else
|
|
443
481
|
File.basename(current_path)
|
|
444
482
|
end
|
|
445
|
-
|
|
483
|
+
details[name] = { path: current_path, branch: branch }
|
|
446
484
|
end
|
|
447
485
|
current_path = nil
|
|
448
486
|
end
|
|
449
487
|
end
|
|
450
488
|
|
|
451
|
-
|
|
489
|
+
details
|
|
452
490
|
end
|
|
453
491
|
end
|
|
454
492
|
|
|
493
|
+
# Backward-compatible { name => path } hash
|
|
494
|
+
def worktree_info
|
|
495
|
+
@worktree_info ||= worktree_details.transform_values { |v| v[:path] }
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# Get branch name for a worktree
|
|
499
|
+
def worktree_branch(tree_name)
|
|
500
|
+
worktree_details.dig(tree_name, :branch)
|
|
501
|
+
end
|
|
502
|
+
|
|
455
503
|
def clear_worktree_cache
|
|
504
|
+
@worktree_details = nil
|
|
456
505
|
@worktree_info = nil
|
|
457
506
|
end
|
|
458
507
|
|
data/bin/h-pr
CHANGED
data/exe/h
CHANGED
data/lib/hiiro/version.rb
CHANGED
data/notes
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
|
|
2
|
+
# Principles to use for refactoring
|
|
3
|
+
|
|
4
|
+
- Separate data from behavior - create domain objects and any relevant value objects
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Goals
|
|
8
|
+
|
|
9
|
+
- same code to manage tasks/subtasks
|
|
10
|
+
- just scoped differently
|
|
11
|
+
- if scope is `:task` then consider all tasks, if scope is :subtask then only consider the tasks associated with the parent task
|
|
12
|
+
- extract data objects
|
|
13
|
+
- so our procedures are human and easy af
|
|
14
|
+
- more composable
|
|
15
|
+
- separate presentation layer from the data
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# features we want
|
|
19
|
+
|
|
20
|
+
- list tasks/subtasks
|
|
21
|
+
- start a new one (wtree/tmux session)
|
|
22
|
+
- switch between them
|
|
23
|
+
- cd to an app's directory
|
|
24
|
+
- stop a task
|
|
25
|
+
- use yml files to track things
|
|
26
|
+
|
|
27
|
+
turn @plugins/tasks.rb into a plugin similar to @plugins/task.rb
|
|
28
|
+
|
|
29
|
+
but in add_subcommands(hiiro) i want to define 2 subtasks:
|
|
30
|
+
1. `:task`
|
|
31
|
+
2. `:subtask`
|
|
32
|
+
|
|
33
|
+
both subcommands should initialize `Tasks.new(hiiro_instance, :task)` or
|
|
34
|
+
`Tasks.new(hiiro_instance, :subtask)` if the subtask subcmd was called
|
|
35
|
+
|
|
36
|
+
then uses the instance of Tasks to either do all the things that it needs to
|
|
37
|
+
do... or just use it to get the data it needs, so that it can do the high level
|
|
38
|
+
things like running tmux or git worktree commands.
|
|
39
|
+
if it doesn't put those things inside Tasks, that would be better, so I can use
|
|
40
|
+
these objects in other places like inside @bin/h-link
|
|
41
|
+
|
|
42
|
+
use the following interface as a guide for how to organize things inside the
|
|
43
|
+
new Tasks plugin
|
|
44
|
+
|
|
45
|
+
# Interface
|
|
46
|
+
|
|
47
|
+
class Environment
|
|
48
|
+
def self.current # => Environment - makes new instance
|
|
49
|
+
def all_tasks # => Task[] - call Task.all and memo cache the result
|
|
50
|
+
def all_sessions # => Session[] - call TmuxSession.all and memo cache the result
|
|
51
|
+
def all_trees # => Tree[] - call Tree.all and memo cache the result
|
|
52
|
+
def task # => Task - move Task.current's behavior into this method, and memo cache the result
|
|
53
|
+
def session # => Session - move TmuxSession.current here and memo cache the result
|
|
54
|
+
def tree # => Tree - move Tree.current here and memo cache the result
|
|
55
|
+
|
|
56
|
+
class Tree
|
|
57
|
+
attr_reader :path, :task, :session
|
|
58
|
+
|
|
59
|
+
def self.current # => Tree - move to Environment#tree
|
|
60
|
+
def self.all # => Tree[]
|
|
61
|
+
|
|
62
|
+
class Session
|
|
63
|
+
attr_reader :name, :task
|
|
64
|
+
|
|
65
|
+
def self.current # => Session - move to Environment#session
|
|
66
|
+
def self.all # => Session[]
|
|
67
|
+
|
|
68
|
+
class Task
|
|
69
|
+
attr_reader :name, :tree, :session, :parent, :subtasks
|
|
70
|
+
|
|
71
|
+
def self.current # => Task - move to Environment#task
|
|
72
|
+
def self.all # => Task[]
|
|
73
|
+
|
|
74
|
+
class App
|
|
75
|
+
attr_reader :name, :path
|
|
76
|
+
def self.current # => App[]
|
|
77
|
+
def self.all # => App[]
|
|
78
|
+
|
|
79
|
+
class Tasks
|
|
80
|
+
attr_reader :hiiro, :scope, :environment
|
|
81
|
+
|
|
82
|
+
def tasks # => Task[]
|
|
83
|
+
def subtasks(task) # => Task[]
|
|
84
|
+
def task_by_name(name) # => Task
|
|
85
|
+
def task_by_tree(tree_name) # => Task
|
|
86
|
+
def task_by_session(session_name) # => Task
|
|
87
|
+
def current_task # => Task
|
|
88
|
+
def current_session # => Session
|
|
89
|
+
def current_tree # => Tree
|
|
90
|
+
|
|
91
|
+
def start_task(name, path) # => Task
|
|
92
|
+
def switch_to_task(task) # => nil
|
|
93
|
+
def stop_task(task) # => nil
|
|
94
|
+
def list(tasks) # => nil # print to screen
|
|
95
|
+
def cd_to_task(task) # => nil
|
|
96
|
+
def cd_to_app(app_name) # => nil
|
|
97
|
+
|
|
98
|
+
class Config
|
|
99
|
+
attr_reader :path
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Follow-up prompts
|
|
105
|
+
|
|
106
|
+
## i forget
|
|
107
|
+
|
|
108
|
+
> when initializing an instance of `Task`...it shouldn't have a `parent_task` or `parent_name` kwarg. it derive that context from the name given. if the name has a `/` in it...then it
|
|
109
|
+
is a subtask and the left side is the parent name and the right side is the subtask name.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## lots of updates
|
|
113
|
+
|
|
114
|
+
here are some updates i'd like to make:
|
|
115
|
+
|
|
116
|
+
- all arguments received from the user should looking that object using
|
|
117
|
+
- something like `start_with?` so they can abbreviate the names of things to
|
|
118
|
+
match
|
|
119
|
+
- the only special one is task_name or subtask_name...if there is a `/` the
|
|
120
|
+
left side will do the abbreviation lookup on a parent name and the right
|
|
121
|
+
side will do a lookup on the subtask name
|
|
122
|
+
- you might want to add `#find_tree(abbreviated_name)` method to
|
|
123
|
+
Environment
|
|
124
|
+
- you might want to add `#find_session(abbreviated_name)` method to
|
|
125
|
+
Environment
|
|
126
|
+
- you might want to add `#find_app(abbreviated_name)` method to
|
|
127
|
+
Environment
|
|
128
|
+
- you might want to add `#find_task(abbreviated_name)` method to
|
|
129
|
+
Environment, where it does the left/right abbreviation lookups
|
|
130
|
+
- these new methods should help clean up and simplify all the lookups in
|
|
131
|
+
the other methods that typically use `.find{ ... }`
|
|
132
|
+
|
|
133
|
+
- get rid of methods like `apps_hash` and in the places that used it...
|
|
134
|
+
- just use the array returned from `#apps` and the attr_reader methods
|
|
135
|
+
for things like name and path or other values that there are attr_readers
|
|
136
|
+
for.
|
|
137
|
+
- always favor an instance method or attr_reader over exporting something
|
|
138
|
+
like hash
|
|
139
|
+
|
|
140
|
+
- `#start_task` shouldn't take an optional `tree_path` keyword arg
|
|
141
|
+
- instead it should take an optional `app_name` kwarg and set the tmux
|
|
142
|
+
session's base directory to the matching app's configured directory
|
|
143
|
+
|
|
144
|
+
- `#switch_task` should also take an optional `app_name` kwarg. if a tmux
|
|
145
|
+
session already exists, just update the configs, associating the task
|
|
146
|
+
with that app. if the tmux session doesn't exist, use the app's path as the
|
|
147
|
+
base_directory.
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
here's one fix i really want to get right.
|
|
151
|
+
|
|
152
|
+
right now TasksPlugin has a dispatch method that uses a case statement to
|
|
153
|
+
handle the subcommands. what i'd like instead is for tasks plugin to initialize
|
|
154
|
+
a new instance of `Hiiro` but instead of it receiving it's usual `*ARGV` as
|
|
155
|
+
positional args, you should pass-in t `*hiiro.args` using the existing `hiiro`
|
|
156
|
+
instance. then for the new `Hiiro` instance...before you call `#run` on
|
|
157
|
+
it...add each of the subcommands defined in the case statement using the
|
|
158
|
+
`add_subcmd` method and have the block passed in be the handler for those
|
|
159
|
+
subcommands
|
|
160
|
+
|
|
161
|
+
so something like this:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
module TasksPlugin
|
|
165
|
+
# ...
|
|
166
|
+
|
|
167
|
+
def self.add_subcommands(hiiro)
|
|
168
|
+
hiiro.add_subcmd(:task) do |*args|
|
|
169
|
+
mgr = Tasks.new(hiiro, scope: :task)
|
|
170
|
+
new_hiiro = task_hiiro(hiiro, mgr)
|
|
171
|
+
new_hiiro.run
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
hiiro.add_subcmd(:subtask) do |*args|
|
|
175
|
+
mgr = Tasks.new(hiiro, scope: :subtask)
|
|
176
|
+
new_hiiro = task_hiiro(hiiro, mgr)
|
|
177
|
+
new_hiiro.run
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def self.task_hiiro(parent_hiiro, mgr)
|
|
182
|
+
Hiiro.init(*parent_hiiro.args, mgr: mgr) do |task_hiiro|
|
|
183
|
+
task_hiiro.add_subcmd(:ls) do |*args|
|
|
184
|
+
# TODO: the logic to print out the task list based on mgr's values
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
task_hiiro.add_subcmd(:start) do |task_name, app_name=nil|
|
|
188
|
+
# TODO: the logic to print out the task list based on mgr's values
|
|
189
|
+
# like:
|
|
190
|
+
mgr.start_task(task_name, app_name: app_name)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
task_hiiro.add_subcmd(:switch) do |task_name, app_name=nil|
|
|
194
|
+
# TODO: the logic to print out the task list based on mgr's values
|
|
195
|
+
mgr.switch_to_task(task_name, app_name: app_name)
|
|
196
|
+
|
|
197
|
+
# but if you need to add logic, i would do it here...like:
|
|
198
|
+
task = mgr.find_task(task_name)
|
|
199
|
+
|
|
200
|
+
if task
|
|
201
|
+
# run cmds to switch to task here...
|
|
202
|
+
else
|
|
203
|
+
# print some msg like no task found
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# ...
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# ...
|
|
212
|
+
end
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## third prompt
|
|
216
|
+
|
|
217
|
+
for things like listing and switching subtasks
|
|
218
|
+
always consider the main/parent task as a potential subtask to switch to.
|
|
219
|
+
for lookups, treat the task name as parent name and `main` as the subtask name
|
|
220
|
+
so something like `nameofparent/main`
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|