abt-cli 0.0.19 → 0.0.24
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/bin/abt +3 -3
- data/lib/abt.rb +6 -6
- data/lib/abt/ari.rb +2 -2
- data/lib/abt/ari_list.rb +1 -1
- data/lib/abt/base_command.rb +7 -7
- data/lib/abt/cli.rb +49 -47
- data/lib/abt/cli/arguments_parser.rb +6 -3
- data/lib/abt/cli/global_commands.rb +23 -0
- data/lib/abt/cli/global_commands/commands.rb +23 -0
- data/lib/abt/cli/global_commands/examples.rb +23 -0
- data/lib/abt/cli/global_commands/help.rb +23 -0
- data/lib/abt/cli/global_commands/readme.rb +23 -0
- data/lib/abt/cli/global_commands/share.rb +36 -0
- data/lib/abt/cli/global_commands/version.rb +23 -0
- data/lib/abt/cli/prompt.rb +64 -52
- data/lib/abt/docs.rb +48 -26
- data/lib/abt/docs/cli.rb +3 -3
- data/lib/abt/docs/markdown.rb +10 -7
- data/lib/abt/git_config.rb +4 -6
- data/lib/abt/helpers.rb +26 -8
- data/lib/abt/providers/asana/api.rb +9 -9
- data/lib/abt/providers/asana/base_command.rb +12 -10
- data/lib/abt/providers/asana/commands/add.rb +13 -12
- data/lib/abt/providers/asana/commands/branch_name.rb +8 -8
- data/lib/abt/providers/asana/commands/clear.rb +7 -8
- data/lib/abt/providers/asana/commands/current.rb +14 -15
- data/lib/abt/providers/asana/commands/finalize.rb +17 -18
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +18 -16
- data/lib/abt/providers/asana/commands/init.rb +8 -41
- data/lib/abt/providers/asana/commands/pick.rb +22 -26
- data/lib/abt/providers/asana/commands/projects.rb +5 -5
- data/lib/abt/providers/asana/commands/share.rb +7 -5
- data/lib/abt/providers/asana/commands/start.rb +28 -21
- data/lib/abt/providers/asana/commands/tasks.rb +6 -6
- data/lib/abt/providers/asana/configuration.rb +37 -29
- data/lib/abt/providers/asana/path.rb +6 -6
- data/lib/abt/providers/devops/api.rb +12 -12
- data/lib/abt/providers/devops/base_command.rb +14 -10
- data/lib/abt/providers/devops/commands/boards.rb +5 -7
- data/lib/abt/providers/devops/commands/branch_name.rb +9 -9
- data/lib/abt/providers/devops/commands/clear.rb +7 -8
- data/lib/abt/providers/devops/commands/current.rb +17 -18
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +21 -19
- data/lib/abt/providers/devops/commands/init.rb +21 -14
- data/lib/abt/providers/devops/commands/pick.rb +25 -19
- data/lib/abt/providers/devops/commands/share.rb +7 -5
- data/lib/abt/providers/devops/commands/{work-items.rb → work_items.rb} +3 -3
- data/lib/abt/providers/devops/configuration.rb +15 -15
- data/lib/abt/providers/devops/path.rb +7 -6
- data/lib/abt/providers/git/commands/branch.rb +23 -21
- data/lib/abt/providers/harvest/api.rb +8 -8
- data/lib/abt/providers/harvest/base_command.rb +10 -8
- data/lib/abt/providers/harvest/commands/clear.rb +7 -8
- data/lib/abt/providers/harvest/commands/current.rb +13 -14
- data/lib/abt/providers/harvest/commands/init.rb +10 -39
- data/lib/abt/providers/harvest/commands/pick.rb +15 -11
- data/lib/abt/providers/harvest/commands/projects.rb +5 -5
- data/lib/abt/providers/harvest/commands/share.rb +7 -5
- data/lib/abt/providers/harvest/commands/start.rb +5 -3
- data/lib/abt/providers/harvest/commands/stop.rb +12 -12
- data/lib/abt/providers/harvest/commands/tasks.rb +7 -7
- data/lib/abt/providers/harvest/commands/track.rb +52 -37
- data/lib/abt/providers/harvest/configuration.rb +18 -18
- data/lib/abt/providers/harvest/path.rb +6 -6
- data/lib/abt/version.rb +1 -1
- metadata +12 -5
| @@ -6,66 +6,33 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Init < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt init asana"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 14 | 
            -
                      end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                      def initialize(cli:, **)
         | 
| 17 | 
            -
                        @config = Configuration.new(cli: cli)
         | 
| 18 | 
            -
                        @cli = cli
         | 
| 13 | 
            +
                        "Pick Asana project for current git repository"
         | 
| 19 14 | 
             
                      end
         | 
| 20 15 |  | 
| 21 16 | 
             
                      def perform
         | 
| 22 | 
            -
                         | 
| 17 | 
            +
                        require_local_config!
         | 
| 23 18 |  | 
| 24 19 | 
             
                        projects # Load projects up front to make it obvious that searches are instant
         | 
| 25 | 
            -
                        project =  | 
| 20 | 
            +
                        project = cli.prompt.search("Select a project", projects)
         | 
| 26 21 |  | 
| 27 | 
            -
                        config.path = Path.from_ids(project[ | 
| 22 | 
            +
                        config.path = Path.from_ids(project_gid: project["gid"])
         | 
| 28 23 |  | 
| 29 24 | 
             
                        print_project(project)
         | 
| 30 25 | 
             
                      end
         | 
| 31 26 |  | 
| 32 27 | 
             
                      private
         | 
| 33 28 |  | 
| 34 | 
            -
                      def find_search_result
         | 
| 35 | 
            -
                        warn 'Select a project'
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                        loop do
         | 
| 38 | 
            -
                          matches = matches_for_string cli.prompt.text('Enter search')
         | 
| 39 | 
            -
                          if matches.empty?
         | 
| 40 | 
            -
                            warn 'No matches'
         | 
| 41 | 
            -
                            next
         | 
| 42 | 
            -
                          end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                          warn 'Showing the 10 first matches' if matches.size > 10
         | 
| 45 | 
            -
                          choice = cli.prompt.choice 'Select a project', matches[0...10], true
         | 
| 46 | 
            -
                          break choice unless choice.nil?
         | 
| 47 | 
            -
                        end
         | 
| 48 | 
            -
                      end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                      def matches_for_string(string)
         | 
| 51 | 
            -
                        search_string = sanitize_string(string)
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                        projects.select do |project|
         | 
| 54 | 
            -
                          sanitize_string(project['name']).include?(search_string)
         | 
| 55 | 
            -
                        end
         | 
| 56 | 
            -
                      end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                      def sanitize_string(string)
         | 
| 59 | 
            -
                        string.downcase.gsub(/[^\w]/, '')
         | 
| 60 | 
            -
                      end
         | 
| 61 | 
            -
             | 
| 62 29 | 
             
                      def projects
         | 
| 63 30 | 
             
                        @projects ||= begin
         | 
| 64 | 
            -
                          warn | 
| 65 | 
            -
                          api.get_paged( | 
| 31 | 
            +
                          warn("Fetching projects...")
         | 
| 32 | 
            +
                          api.get_paged("projects",
         | 
| 66 33 | 
             
                                        workspace: config.workspace_gid,
         | 
| 67 34 | 
             
                                        archived: false,
         | 
| 68 | 
            -
                                        opt_fields:  | 
| 35 | 
            +
                                        opt_fields: "name,permalink_url")
         | 
| 69 36 | 
             
                        end
         | 
| 70 37 | 
             
                      end
         | 
| 71 38 | 
             
                    end
         | 
| @@ -6,72 +6,68 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Pick < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt pick asana[:<project-gid>]"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        "Pick task for current git repository"
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def self.flags
         | 
| 17 17 | 
             
                        [
         | 
| 18 | 
            -
                          [ | 
| 18 | 
            +
                          ["-d", "--dry-run", "Keep existing configuration"]
         | 
| 19 19 | 
             
                        ]
         | 
| 20 20 | 
             
                      end
         | 
| 21 21 |  | 
| 22 22 | 
             
                      def perform
         | 
| 23 | 
            -
                         | 
| 23 | 
            +
                        require_local_config!
         | 
| 24 24 | 
             
                        require_project!
         | 
| 25 25 |  | 
| 26 | 
            -
                        warn | 
| 27 | 
            -
             | 
| 26 | 
            +
                        warn(project["name"])
         | 
| 28 27 | 
             
                        task = select_task
         | 
| 29 28 |  | 
| 30 29 | 
             
                        print_task(project, task)
         | 
| 31 30 |  | 
| 32 31 | 
             
                        return if flags[:"dry-run"]
         | 
| 33 32 |  | 
| 34 | 
            -
                        config.path = Path.from_ids(project_gid, task[ | 
| 33 | 
            +
                        config.path = Path.from_ids(project_gid: project_gid, task_gid: task["gid"])
         | 
| 35 34 | 
             
                      end
         | 
| 36 35 |  | 
| 37 36 | 
             
                      private
         | 
| 38 37 |  | 
| 39 38 | 
             
                      def project
         | 
| 40 | 
            -
                        @project ||= api.get("projects/#{project_gid}", opt_fields:  | 
| 39 | 
            +
                        @project ||= api.get("projects/#{project_gid}", opt_fields: "name")
         | 
| 41 40 | 
             
                      end
         | 
| 42 41 |  | 
| 43 42 | 
             
                      def select_task
         | 
| 44 | 
            -
                         | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
                           | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
                           | 
| 53 | 
            -
             | 
| 54 | 
            -
                          task = cli.prompt.choice 'Select a task', tasks, true
         | 
| 55 | 
            -
                          return task if task
         | 
| 43 | 
            +
                        section = cli.prompt.choice("Which section?", sections)
         | 
| 44 | 
            +
                        warn("Fetching tasks...")
         | 
| 45 | 
            +
                        tasks = tasks_in_section(section)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                        if tasks.length.zero?
         | 
| 48 | 
            +
                          warn("Section is empty")
         | 
| 49 | 
            +
                          select_task
         | 
| 50 | 
            +
                        else
         | 
| 51 | 
            +
                          cli.prompt.choice("Select a task", tasks, nil_option: true) || select_task
         | 
| 56 52 | 
             
                        end
         | 
| 57 53 | 
             
                      end
         | 
| 58 54 |  | 
| 59 55 | 
             
                      def tasks_in_section(section)
         | 
| 60 56 | 
             
                        tasks = api.get_paged(
         | 
| 61 | 
            -
                           | 
| 62 | 
            -
                          section: section[ | 
| 63 | 
            -
                          opt_fields:  | 
| 57 | 
            +
                          "tasks",
         | 
| 58 | 
            +
                          section: section["gid"],
         | 
| 59 | 
            +
                          opt_fields: "name,completed,permalink_url"
         | 
| 64 60 | 
             
                        )
         | 
| 65 61 |  | 
| 66 62 | 
             
                        # The below filtering is the best we can do with Asanas api, see this:
         | 
| 67 63 | 
             
                        # https://forum.asana.com/t/tasks-query-completed-since-is-broken-for-sections/21461
         | 
| 68 | 
            -
                        tasks. | 
| 64 | 
            +
                        tasks.reject { |task| task["completed"] }
         | 
| 69 65 | 
             
                      end
         | 
| 70 66 |  | 
| 71 67 | 
             
                      def sections
         | 
| 72 68 | 
             
                        @sections ||= begin
         | 
| 73 | 
            -
                          warn | 
| 74 | 
            -
                          api.get_paged("projects/#{project_gid}/sections", opt_fields:  | 
| 69 | 
            +
                          warn("Fetching sections...")
         | 
| 70 | 
            +
                          api.get_paged("projects/#{project_gid}/sections", opt_fields: "name")
         | 
| 75 71 | 
             
                        end
         | 
| 76 72 | 
             
                      end
         | 
| 77 73 | 
             
                    end
         | 
| @@ -6,11 +6,11 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Projects < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt projects asana"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        "List all available projects - useful for piping into grep etc."
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def perform
         | 
| @@ -23,12 +23,12 @@ module Abt | |
| 23 23 |  | 
| 24 24 | 
             
                      def projects
         | 
| 25 25 | 
             
                        @projects ||= begin
         | 
| 26 | 
            -
                          warn | 
| 26 | 
            +
                          warn("Fetching projects...")
         | 
| 27 27 | 
             
                          api.get_paged(
         | 
| 28 | 
            -
                             | 
| 28 | 
            +
                            "projects",
         | 
| 29 29 | 
             
                            workspace: config.workspace_gid,
         | 
| 30 30 | 
             
                            archived: false,
         | 
| 31 | 
            -
                            opt_fields:  | 
| 31 | 
            +
                            opt_fields: "name"
         | 
| 32 32 | 
             
                          )
         | 
| 33 33 | 
             
                        end
         | 
| 34 34 | 
             
                      end
         | 
| @@ -6,17 +6,19 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Share < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt share asana[:<project-gid>[/<task-gid>]]"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        "Print project/task ARI"
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def perform
         | 
| 17 | 
            -
                         | 
| 18 | 
            -
             | 
| 19 | 
            -
                        cli. | 
| 17 | 
            +
                        if path != ""
         | 
| 18 | 
            +
                          cli.print_ari("asana", path)
         | 
| 19 | 
            +
                        elsif cli.output.isatty
         | 
| 20 | 
            +
                          warn("No configuration for project. Did you initialize Asana?")
         | 
| 21 | 
            +
                        end
         | 
| 20 22 | 
             
                      end
         | 
| 21 23 | 
             
                    end
         | 
| 22 24 | 
             
                  end
         | 
| @@ -6,16 +6,16 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Start < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt start asana[:<project-gid>/<task-gid>]"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        "Move current or specified task to WIP section (column) and assign it to you"
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def self.flags
         | 
| 17 17 | 
             
                        [
         | 
| 18 | 
            -
                          [ | 
| 18 | 
            +
                          ["-s", "--set", "Set specified task as current"]
         | 
| 19 19 | 
             
                        ]
         | 
| 20 20 | 
             
                      end
         | 
| 21 21 |  | 
| @@ -38,33 +38,39 @@ module Abt | |
| 38 38 | 
             
                        return unless config.local_available?
         | 
| 39 39 |  | 
| 40 40 | 
             
                        config.path = path
         | 
| 41 | 
            -
                        warn | 
| 41 | 
            +
                        warn("Current task updated")
         | 
| 42 42 | 
             
                      end
         | 
| 43 43 |  | 
| 44 44 | 
             
                      def update_assignee_if_needed
         | 
| 45 | 
            -
                        current_assignee = task.dig('assignee')
         | 
| 46 | 
            -
             | 
| 47 45 | 
             
                        if current_assignee.nil?
         | 
| 48 | 
            -
                          warn | 
| 46 | 
            +
                          warn("Assigning task to user: #{current_user['name']}")
         | 
| 49 47 | 
             
                          update_assignee
         | 
| 50 | 
            -
                        elsif current_assignee[ | 
| 51 | 
            -
                          warn | 
| 52 | 
            -
                        elsif  | 
| 53 | 
            -
                          warn | 
| 48 | 
            +
                        elsif current_assignee["gid"] == current_user["gid"]
         | 
| 49 | 
            +
                          warn("You are already assigned to this task")
         | 
| 50 | 
            +
                        elsif should_reassign?
         | 
| 51 | 
            +
                          warn("Reassigning task to user: #{current_user['name']}")
         | 
| 54 52 | 
             
                          update_assignee
         | 
| 55 53 | 
             
                        end
         | 
| 56 54 | 
             
                      end
         | 
| 57 55 |  | 
| 56 | 
            +
                      def current_assignee
         | 
| 57 | 
            +
                        task["assignee"]
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      def should_reassign?
         | 
| 61 | 
            +
                        cli.prompt.boolean("Task is assigned to: #{current_assignee['name']}, take over?")
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
             | 
| 58 64 | 
             
                      def move_if_needed
         | 
| 59 65 | 
             
                        unless project_gid == config.path.project_gid
         | 
| 60 | 
            -
                          warn | 
| 66 | 
            +
                          warn("Task was not moved, this is not implemented for tasks outside current project")
         | 
| 61 67 | 
             
                          return
         | 
| 62 68 | 
             
                        end
         | 
| 63 69 |  | 
| 64 70 | 
             
                        if task_already_in_wip_section?
         | 
| 65 | 
            -
                          warn | 
| 71 | 
            +
                          warn("Task already in section: #{current_task_section['name']}")
         | 
| 66 72 | 
             
                        else
         | 
| 67 | 
            -
                          warn | 
| 73 | 
            +
                          warn("Moving task to section: #{wip_section['name']}")
         | 
| 68 74 | 
             
                          move_task
         | 
| 69 75 | 
             
                        end
         | 
| 70 76 | 
             
                      end
         | 
| @@ -74,17 +80,17 @@ module Abt | |
| 74 80 | 
             
                      end
         | 
| 75 81 |  | 
| 76 82 | 
             
                      def current_task_section
         | 
| 77 | 
            -
                        task_section_membership&.dig( | 
| 83 | 
            +
                        task_section_membership&.dig("section")
         | 
| 78 84 | 
             
                      end
         | 
| 79 85 |  | 
| 80 86 | 
             
                      def task_section_membership
         | 
| 81 | 
            -
                        task[ | 
| 82 | 
            -
                          membership.dig( | 
| 87 | 
            +
                        task["memberships"].find do |membership|
         | 
| 88 | 
            +
                          membership.dig("section", "gid") == config.wip_section_gid
         | 
| 83 89 | 
             
                        end
         | 
| 84 90 | 
             
                      end
         | 
| 85 91 |  | 
| 86 92 | 
             
                      def wip_section
         | 
| 87 | 
            -
                        @wip_section ||= api.get("sections/#{config.wip_section_gid}", opt_fields:  | 
| 93 | 
            +
                        @wip_section ||= api.get("sections/#{config.wip_section_gid}", opt_fields: "name")
         | 
| 88 94 | 
             
                      end
         | 
| 89 95 |  | 
| 90 96 | 
             
                      def move_task
         | 
| @@ -94,17 +100,18 @@ module Abt | |
| 94 100 | 
             
                      end
         | 
| 95 101 |  | 
| 96 102 | 
             
                      def update_assignee
         | 
| 97 | 
            -
                        body = { data: { assignee: current_user[ | 
| 103 | 
            +
                        body = { data: { assignee: current_user["gid"] } }
         | 
| 98 104 | 
             
                        body_json = Oj.dump(body, mode: :json)
         | 
| 99 105 | 
             
                        api.put("tasks/#{task_gid}", body_json)
         | 
| 100 106 | 
             
                      end
         | 
| 101 107 |  | 
| 102 108 | 
             
                      def current_user
         | 
| 103 | 
            -
                        @current_user ||= api.get( | 
| 109 | 
            +
                        @current_user ||= api.get("users/me", opt_fields: "name")
         | 
| 104 110 | 
             
                      end
         | 
| 105 111 |  | 
| 106 112 | 
             
                      def task
         | 
| 107 | 
            -
                        @task ||= api.get("tasks/#{task_gid}", | 
| 113 | 
            +
                        @task ||= api.get("tasks/#{task_gid}",
         | 
| 114 | 
            +
                                          opt_fields: "name,memberships.section.name,assignee.name,permalink_url")
         | 
| 108 115 | 
             
                      end
         | 
| 109 116 | 
             
                    end
         | 
| 110 117 | 
             
                  end
         | 
| @@ -6,11 +6,11 @@ module Abt | |
| 6 6 | 
             
                  module Commands
         | 
| 7 7 | 
             
                    class Tasks < BaseCommand
         | 
| 8 8 | 
             
                      def self.usage
         | 
| 9 | 
            -
                         | 
| 9 | 
            +
                        "abt tasks asana"
         | 
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.description
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        "List available tasks on project - useful for piping into grep etc."
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def perform
         | 
| @@ -25,15 +25,15 @@ module Abt | |
| 25 25 |  | 
| 26 26 | 
             
                      def project
         | 
| 27 27 | 
             
                        @project ||= begin
         | 
| 28 | 
            -
                          api.get("projects/#{project_gid}", opt_fields:  | 
| 28 | 
            +
                          api.get("projects/#{project_gid}", opt_fields: "name")
         | 
| 29 29 | 
             
                        end
         | 
| 30 30 | 
             
                      end
         | 
| 31 31 |  | 
| 32 32 | 
             
                      def tasks
         | 
| 33 33 | 
             
                        @tasks ||= begin
         | 
| 34 | 
            -
                          warn | 
| 35 | 
            -
                          tasks = api.get_paged( | 
| 36 | 
            -
                          tasks. | 
| 34 | 
            +
                          warn("Fetching tasks...")
         | 
| 35 | 
            +
                          tasks = api.get_paged("tasks", project: project["gid"], opt_fields: "name,completed")
         | 
| 36 | 
            +
                          tasks.reject { |task| task["completed"] }
         | 
| 37 37 | 
             
                        end
         | 
| 38 38 | 
             
                      end
         | 
| 39 39 | 
             
                    end
         | 
| @@ -15,18 +15,18 @@ module Abt | |
| 15 15 | 
             
                    end
         | 
| 16 16 |  | 
| 17 17 | 
             
                    def path
         | 
| 18 | 
            -
                      Path.new(local_available? && git[ | 
| 18 | 
            +
                      Path.new(local_available? && git["path"] || "")
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 |  | 
| 21 21 | 
             
                    def path=(new_path)
         | 
| 22 | 
            -
                      git[ | 
| 22 | 
            +
                      git["path"] = new_path
         | 
| 23 23 | 
             
                    end
         | 
| 24 24 |  | 
| 25 25 | 
             
                    def workspace_gid
         | 
| 26 26 | 
             
                      @workspace_gid ||= begin
         | 
| 27 | 
            -
                        current = git_global[ | 
| 27 | 
            +
                        current = git_global["workspaceGid"]
         | 
| 28 28 | 
             
                        if current.nil?
         | 
| 29 | 
            -
                           | 
| 29 | 
            +
                          prompt_workspace_gid
         | 
| 30 30 | 
             
                        else
         | 
| 31 31 | 
             
                          current
         | 
| 32 32 | 
             
                        end
         | 
| @@ -36,13 +36,13 @@ module Abt | |
| 36 36 | 
             
                    def wip_section_gid
         | 
| 37 37 | 
             
                      return nil unless local_available?
         | 
| 38 38 |  | 
| 39 | 
            -
                      @wip_section_gid ||= git[ | 
| 39 | 
            +
                      @wip_section_gid ||= git["wipSectionGid"] || prompt_wip_section["gid"]
         | 
| 40 40 | 
             
                    end
         | 
| 41 41 |  | 
| 42 42 | 
             
                    def finalized_section_gid
         | 
| 43 43 | 
             
                      return nil unless local_available?
         | 
| 44 44 |  | 
| 45 | 
            -
                      @finalized_section_gid ||= git[ | 
| 45 | 
            +
                      @finalized_section_gid ||= git["finalizedSectionGid"] || prompt_finalized_section["gid"]
         | 
| 46 46 | 
             
                    end
         | 
| 47 47 |  | 
| 48 48 | 
             
                    def clear_local(verbose: true)
         | 
| @@ -54,58 +54,66 @@ module Abt | |
| 54 54 | 
             
                    end
         | 
| 55 55 |  | 
| 56 56 | 
             
                    def access_token
         | 
| 57 | 
            -
                      return git_global[ | 
| 57 | 
            +
                      return git_global["accessToken"] unless git_global["accessToken"].nil?
         | 
| 58 58 |  | 
| 59 | 
            -
                      git_global[ | 
| 60 | 
            -
                         | 
| 61 | 
            -
                         | 
| 62 | 
            -
                         | 
| 63 | 
            -
                         | 
| 59 | 
            +
                      git_global["accessToken"] = cli.prompt.text([
         | 
| 60 | 
            +
                        "Please provide your personal access token for Asana.",
         | 
| 61 | 
            +
                        "If you don't have one, create one here: https://app.asana.com/0/developer-console",
         | 
| 62 | 
            +
                        "",
         | 
| 63 | 
            +
                        "Enter access token"
         | 
| 64 64 | 
             
                      ].join("\n"))
         | 
| 65 65 | 
             
                    end
         | 
| 66 66 |  | 
| 67 67 | 
             
                    private
         | 
| 68 68 |  | 
| 69 69 | 
             
                    def git
         | 
| 70 | 
            -
                      @git ||= GitConfig.new( | 
| 70 | 
            +
                      @git ||= GitConfig.new("local", "abt.asana")
         | 
| 71 71 | 
             
                    end
         | 
| 72 72 |  | 
| 73 73 | 
             
                    def git_global
         | 
| 74 | 
            -
                      @git_global ||= GitConfig.new( | 
| 74 | 
            +
                      @git_global ||= GitConfig.new("global", "abt.asana")
         | 
| 75 75 | 
             
                    end
         | 
| 76 76 |  | 
| 77 77 | 
             
                    def prompt_finalized_section
         | 
| 78 78 | 
             
                      section = prompt_section('Select section for finalized tasks (E.g. "Merged")')
         | 
| 79 | 
            -
                      git[ | 
| 79 | 
            +
                      git["finalizedSectionGid"] = section["gid"]
         | 
| 80 80 | 
             
                      section
         | 
| 81 81 | 
             
                    end
         | 
| 82 82 |  | 
| 83 83 | 
             
                    def prompt_wip_section
         | 
| 84 | 
            -
                      section = prompt_section( | 
| 85 | 
            -
                      git[ | 
| 84 | 
            +
                      section = prompt_section("Select WIP (Work In Progress) section")
         | 
| 85 | 
            +
                      git["wipSectionGid"] = section["gid"]
         | 
| 86 86 | 
             
                      section
         | 
| 87 87 | 
             
                    end
         | 
| 88 88 |  | 
| 89 89 | 
             
                    def prompt_section(message)
         | 
| 90 | 
            -
                      cli.warn | 
| 91 | 
            -
                      sections = api.get_paged("projects/#{path.project_gid}/sections")
         | 
| 90 | 
            +
                      cli.warn("Fetching sections...")
         | 
| 91 | 
            +
                      sections = api.get_paged("projects/#{path.project_gid}/sections", opt_fields: "name")
         | 
| 92 92 | 
             
                      cli.prompt.choice(message, sections)
         | 
| 93 93 | 
             
                    end
         | 
| 94 94 |  | 
| 95 | 
            -
                    def  | 
| 96 | 
            -
                      cli. | 
| 97 | 
            -
             | 
| 98 | 
            -
                      if workspaces. | 
| 99 | 
            -
                        cli.abort 'Your asana access token does not have access to any workspaces'
         | 
| 100 | 
            -
                      elsif workspaces.one?
         | 
| 95 | 
            +
                    def prompt_workspace_gid
         | 
| 96 | 
            +
                      cli.abort("Your asana access token does not have access to any workspaces") if workspaces.empty?
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      if workspaces.one?
         | 
| 101 99 | 
             
                        workspace = workspaces.first
         | 
| 102 | 
            -
                        cli.warn | 
| 100 | 
            +
                        cli.warn("Selected Asana workspace: #{workspace['name']}")
         | 
| 103 101 | 
             
                      else
         | 
| 104 | 
            -
                        workspace =  | 
| 102 | 
            +
                        workspace = pick_workspace
         | 
| 105 103 | 
             
                      end
         | 
| 106 104 |  | 
| 107 | 
            -
                      git_global[ | 
| 108 | 
            -
             | 
| 105 | 
            +
                      git_global["workspaceGid"] = workspace["gid"]
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    def pick_workspace
         | 
| 109 | 
            +
                      cli.prompt.choice("Select Asana workspace", workspaces)
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    def workspaces
         | 
| 113 | 
            +
                      @workspaces ||= begin
         | 
| 114 | 
            +
                        cli.warn("Fetching workspaces...")
         | 
| 115 | 
            +
                        api.get_paged("workspaces", opt_fields: "name")
         | 
| 116 | 
            +
                      end
         | 
| 109 117 | 
             
                    end
         | 
| 110 118 |  | 
| 111 119 | 
             
                    def api
         |