Module: ShapeupCli
- Defined in:
- lib/shapeup_cli.rb,
lib/shapeup_cli/auth.rb,
lib/shapeup_cli/client.rb,
lib/shapeup_cli/config.rb,
lib/shapeup_cli/output.rb,
lib/shapeup_cli/surface.rb,
lib/shapeup_cli/version.rb,
lib/shapeup_cli/commands.rb,
lib/shapeup_cli/commands/auth.rb,
lib/shapeup_cli/commands/base.rb,
lib/shapeup_cli/commands/orgs.rb,
lib/shapeup_cli/commands/tags.rb,
lib/shapeup_cli/commands/cycle.rb,
lib/shapeup_cli/commands/login.rb,
lib/shapeup_cli/commands/setup.rb,
lib/shapeup_cli/commands/tasks.rb,
lib/shapeup_cli/commands/doctor.rb,
lib/shapeup_cli/commands/issues.rb,
lib/shapeup_cli/commands/logout.rb,
lib/shapeup_cli/commands/scopes.rb,
lib/shapeup_cli/commands/search.rb,
lib/shapeup_cli/commands/my_work.rb,
lib/shapeup_cli/commands/pitches.rb,
lib/shapeup_cli/commands/streams.rb,
lib/shapeup_cli/commands/comments.rb,
lib/shapeup_cli/commands/checklist.rb,
lib/shapeup_cli/commands/config_cmd.rb
Defined Under Namespace
Modules: Auth, Commands, Config, Output, Surface Classes: Client
Constant Summary collapse
- DEFAULT_HOST =
"https://shapeup.cc"- EXIT_OK =
Exit codes, matching the rubric shared by the Basecamp-family CLIs so agents that know one CLI can read all of them.
0- EXIT_USAGE =
1- EXIT_NOT_FOUND =
2- EXIT_AUTH =
3- EXIT_PERMISSION =
4- EXIT_RATE_LIMIT =
5- EXIT_NETWORK =
6- EXIT_API_ERROR =
7- EXIT_AMBIGUOUS =
8- EXIT_INTERRUPTED =
130- COMMAND_MAP =
{ "orgs" => Commands::Orgs, "pitches" => Commands::Pitches, "cycle" => Commands::Cycle, "streams" => Commands::Streams, "scopes" => Commands::Scopes, "tasks" => Commands::Tasks, "issues" => Commands::Issues, "my-work" => Commands::MyWork, "search" => Commands::Search, "auth" => Commands::Auth, "config" => Commands::ConfigCmd, "doctor" => Commands::Doctor, "setup" => Commands::Setup, "comments" => Commands::Comments, "checklist" => Commands::Checklist, "tags" => Commands::Tags }.freeze
- VERSION =
"0.7.0"
Class Method Summary collapse
-
.fail_with(message, code:, exit_code:, retryable:, hint: nil) ⇒ Object
Errors reach machine consumers as a JSON envelope on stdout with a
retryableverdict, and humans as plain text on stderr. - .run(argv) ⇒ Object
- .top_level_metadata ⇒ Object
Class Method Details
.fail_with(message, code:, exit_code:, retryable:, hint: nil) ⇒ Object
Errors reach machine consumers as a JSON envelope on stdout with a
retryable verdict, and humans as plain text on stderr. retryable: false
means "no known reason a retry helps", not proof of permanence.
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/shapeup_cli.rb', line 130 def self.fail_with(, code:, exit_code:, retryable:, hint: nil) if @machine_output envelope = { ok: false, error: , code: code, retryable: retryable } envelope[:hint] = hint if hint puts JSON.generate(envelope) else $stderr.puts $stderr.puts hint if hint end exit exit_code end |
.run(argv) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/shapeup_cli.rb', line 54 def self.run(argv) args = argv.dup @machine_output = args.intersect?(%w[ --json --agent --quiet -q ]) || !$stdout.tty? # Top-level: shapeup --agent --help if args.include?("--agent") && args.include?("--help") command = (args - [ "--agent", "--help" ]).first if command && COMMAND_MAP[command] puts JSON.pretty_generate(COMMAND_MAP[command].) else puts JSON.pretty_generate() end return end command = args.shift case command when "login" then Commands::Login.run(args) when "logout" then Commands::Logout.run(args) when "auth" then Commands::Auth.run(args) when "orgs" then Commands::Orgs.run(args) when "pitches" then Commands::Pitches.run(args) when "pitch" then Commands::Pitches.run([ "show" ] + args) when "cycle" then Commands::Cycle.run(args) when "cycles" then Commands::Cycle.run([ "list" ] + args) when "streams" then Commands::Streams.run(args) when "scopes" then Commands::Scopes.run(args) when "tasks" then Commands::Tasks.run(args) when "todo" then Commands::Tasks.run([ "create" ] + args) when "done" then Commands::Tasks.run([ "complete" ] + args) when "undone" then Commands::Tasks.run([ "uncomplete" ] + args) when "issues" then Commands::Issues.run(args) when "issue" then Commands::Issues.run([ "show" ] + args) when "watching" then Commands::Issues.run([ "watching" ] + args) when "comments" then Commands::Comments.run(args) when "checklist" then Commands::Checklist.run(args) when "tags" then Commands::Tags.run(args) when "my-work", "me" then Commands::MyWork.run(args) when "search" then Commands::Search.run(args) when "config" then Commands::ConfigCmd.run(args) when "doctor" then Commands::Doctor.run(args) when "setup" then Commands::Setup.run(args) when "commands" then Commands.list_commands when "version", "-v", "--version" puts "shapeup #{VERSION}" when "help", "-h", "--help", nil Commands.help else $stderr.puts "Unknown command: #{command}" $stderr.puts "Run 'shapeup help' for usage" exit EXIT_USAGE end rescue Client::AuthError fail_with "Not authenticated.", code: "auth_required", exit_code: EXIT_AUTH, retryable: false, hint: "Run 'shapeup login' or set SHAPEUP_TOKEN" rescue Client::NotFoundError => e fail_with "Not found: #{e.}", code: "not_found", exit_code: EXIT_NOT_FOUND, retryable: false rescue Client::PermissionError => e fail_with "Access denied: #{e.}", code: "forbidden", exit_code: EXIT_PERMISSION, retryable: false rescue Client::RateLimitError fail_with "Rate limited.", code: "rate_limit", exit_code: EXIT_RATE_LIMIT, retryable: true, hint: "Wait a moment and retry" rescue Client::NetworkError => e fail_with e., code: "network", exit_code: EXIT_NETWORK, retryable: true, hint: "Check your connection and 'shapeup config show'" rescue Client::ApiError => e fail_with "Error: #{e.}", code: "api_error", exit_code: EXIT_API_ERROR, retryable: false rescue Interrupt $stderr.puts "\nAborted." exit EXIT_INTERRUPTED end |
.top_level_metadata ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/shapeup_cli.rb', line 142 def self. { command: "shapeup", version: VERSION, short: "Manage ShapeUp pitches, scopes, tasks, issues, and cycles from the terminal", commands: COMMAND_MAP.map { |name, klass| { name: name, **klass..slice(:short, :path) } }, shortcuts: { "pitch <id>" => "pitches show <id>", "cycles" => "cycle list", "todo \"...\"" => "tasks create \"...\"", "done <id>" => "tasks complete <id>", "undone <id>" => "tasks uncomplete <id>", "issue <id>" => "issues show <id>", "watching" => "issues watching", "me" => "my-work" }, inherited_flags: [ { name: "org", type: "string", usage: "Organisation ID or name" }, { name: "json", type: "bool", usage: "Full JSON envelope with breadcrumbs" }, { name: "md", type: "bool", usage: "Markdown output" }, { name: "agent", type: "bool", usage: "Raw JSON data only (for AI agents)" } ] } end |