shapeup-cli 0.3.4 → 0.4.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 +4 -4
- data/lib/shapeup_cli/commands/base.rb +23 -0
- data/lib/shapeup_cli/commands/comments.rb +27 -2
- data/lib/shapeup_cli/commands/issues.rb +5 -3
- data/lib/shapeup_cli/commands/pitches.rb +84 -5
- data/lib/shapeup_cli/commands/scopes.rb +19 -2
- data/lib/shapeup_cli/commands/streams.rb +66 -0
- data/lib/shapeup_cli/commands/tasks.rb +66 -8
- data/lib/shapeup_cli/commands.rb +28 -6
- data/lib/shapeup_cli/output.rb +3 -3
- data/lib/shapeup_cli.rb +11 -7
- data/skills/shapeup/SKILL.md +15 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cddf715cbc5ec20b37a27a84a33daa1fcbe1c16a959b8dac6cf45645d5f40adc
|
|
4
|
+
data.tar.gz: 7de65ac1329b084a8b43e10eeb59e63f6699b9a7763dc2ea8b76b52d4dc9d4b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d68e45dda572611c45d9ff32efdb52834016d793d2c919da995326a786b618741f7904d807983bb3b396f418f0c32acef7356f4cda83f84bc2ca1340ea51edf
|
|
7
|
+
data.tar.gz: 32716fe62a9838e31c4485336d524aca1ba0e07400f06af72811e12565d9ebbae6f17cf323cecf5757e05dc04d0748a3f7d985e85c2b7be83df31dc994dd8eb8
|
|
@@ -114,6 +114,29 @@ module ShapeupCli
|
|
|
114
114
|
!!@remaining.delete(flag)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
# Consume a confirmation bypass flag (--yes / -y). Call before reading
|
|
118
|
+
# positionals so the flag isn't mistaken for one.
|
|
119
|
+
def assume_yes?
|
|
120
|
+
# both forms; -y does not start with "--" so it must be consumed explicitly
|
|
121
|
+
consume_flag("--yes") | consume_flag("-y")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Guard a destructive action. `assume_yes` skips the prompt (the caller
|
|
125
|
+
# passes the result of assume_yes?). With a TTY we ask for confirmation;
|
|
126
|
+
# without one we refuse rather than delete unattended — so agents and
|
|
127
|
+
# scripts must opt in explicitly with --yes.
|
|
128
|
+
def confirm_destructive!(action, assume_yes)
|
|
129
|
+
return if assume_yes
|
|
130
|
+
|
|
131
|
+
unless $stdin.tty?
|
|
132
|
+
abort "Refusing to #{action} without confirmation. Re-run with --yes to proceed."
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
$stderr.print "#{action}? This cannot be undone [y/N]: "
|
|
136
|
+
answer = $stdin.gets&.strip&.downcase
|
|
137
|
+
abort "Aborted." unless answer == "y" || answer == "yes"
|
|
138
|
+
end
|
|
139
|
+
|
|
117
140
|
# Parse --comments/--no-comments + --comments-limit N into MCP args.
|
|
118
141
|
def comment_flags
|
|
119
142
|
args = {}
|
|
@@ -10,7 +10,9 @@ module ShapeupCli
|
|
|
10
10
|
short: "List and add comments on issues, pitches, scopes, and tasks",
|
|
11
11
|
subcommands: [
|
|
12
12
|
{ name: "list", short: "List comments", path: "shapeup comments list --issue <id>" },
|
|
13
|
-
{ name: "add", short: "Add a comment", path: 'shapeup comments add --issue <id> "Comment text"' }
|
|
13
|
+
{ name: "add", short: "Add a comment", path: 'shapeup comments add --issue <id> "Comment text"' },
|
|
14
|
+
{ name: "edit", short: "Edit your own comment", path: 'shapeup comments edit <comment_id> "New text"' },
|
|
15
|
+
{ name: "remove", short: "Delete your own comment", path: "shapeup comments remove <comment_id>" }
|
|
14
16
|
],
|
|
15
17
|
flags: [
|
|
16
18
|
{ name: "issue", type: "string", usage: "Issue ID" },
|
|
@@ -22,7 +24,9 @@ module ShapeupCli
|
|
|
22
24
|
"shapeup comments list --issue 42",
|
|
23
25
|
'shapeup comments add --issue 42 "Investigated — this is a CSS issue in the navbar"',
|
|
24
26
|
"shapeup comments list --pitch 10",
|
|
25
|
-
'shapeup comments add --pitch 10 "Shaped and ready for betting"'
|
|
27
|
+
'shapeup comments add --pitch 10 "Shaped and ready for betting"',
|
|
28
|
+
'shapeup comments edit 88 "Updated: this is a navbar z-index issue"',
|
|
29
|
+
"shapeup comments remove 88"
|
|
26
30
|
]
|
|
27
31
|
}
|
|
28
32
|
end
|
|
@@ -32,6 +36,8 @@ module ShapeupCli
|
|
|
32
36
|
|
|
33
37
|
case subcommand
|
|
34
38
|
when "add" then add
|
|
39
|
+
when "edit" then edit
|
|
40
|
+
when "remove" then remove
|
|
35
41
|
when "list", nil then list
|
|
36
42
|
else list
|
|
37
43
|
end
|
|
@@ -63,6 +69,25 @@ module ShapeupCli
|
|
|
63
69
|
]
|
|
64
70
|
end
|
|
65
71
|
|
|
72
|
+
def edit
|
|
73
|
+
comment_id = positional_arg(1) || abort('Usage: shapeup comments edit <comment_id> "New text"')
|
|
74
|
+
text = positional_arg(2) || abort('Usage: shapeup comments edit <comment_id> "New text"')
|
|
75
|
+
|
|
76
|
+
result = call_tool("update_comment", comment_id: comment_id.to_s, content: text)
|
|
77
|
+
|
|
78
|
+
render result, summary: "Comment ##{comment_id} updated"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def remove
|
|
82
|
+
yes = assume_yes?
|
|
83
|
+
comment_id = positional_arg(1) || abort("Usage: shapeup comments remove <comment_id> [--yes]")
|
|
84
|
+
confirm_destructive!("Delete comment ##{comment_id}", yes)
|
|
85
|
+
|
|
86
|
+
result = call_tool("delete_comment", comment_id: comment_id.to_s)
|
|
87
|
+
|
|
88
|
+
render result, summary: "Comment ##{comment_id} deleted"
|
|
89
|
+
end
|
|
90
|
+
|
|
66
91
|
def resolve_commentable
|
|
67
92
|
issue = extract_option("--issue")
|
|
68
93
|
pitch = extract_option("--pitch")
|
|
@@ -331,7 +331,9 @@ module ShapeupCli
|
|
|
331
331
|
end
|
|
332
332
|
|
|
333
333
|
def delete
|
|
334
|
-
|
|
334
|
+
yes = assume_yes?
|
|
335
|
+
id = positional_arg(1) || abort("Usage: shapeup issues delete <id> [--yes]")
|
|
336
|
+
confirm_destructive!("Delete issue ##{id}", yes)
|
|
335
337
|
|
|
336
338
|
result = call_tool("delete_issue", issue: id.to_s)
|
|
337
339
|
|
|
@@ -356,8 +358,8 @@ module ShapeupCli
|
|
|
356
358
|
end
|
|
357
359
|
|
|
358
360
|
def add_to_pitch
|
|
359
|
-
id = positional_arg(1) || abort(
|
|
360
|
-
pitch_id = extract_option("--pitch") || abort(
|
|
361
|
+
id = positional_arg(1) || abort("Usage: shapeup issues add-to-pitch <id> --pitch <pitch_id>")
|
|
362
|
+
pitch_id = extract_option("--pitch") || abort("Usage: shapeup issues add-to-pitch <id> --pitch <pitch_id>")
|
|
361
363
|
|
|
362
364
|
result = call_tool("add_issue_to_pitch", issue: id.to_s, package: pitch_id.to_s)
|
|
363
365
|
|
|
@@ -12,16 +12,23 @@ module ShapeupCli
|
|
|
12
12
|
{ name: "list", short: "List pitches (default)", path: "shapeup pitches list" },
|
|
13
13
|
{ name: "show", short: "Show pitch details with scopes and tasks", path: "shapeup pitches show <id>" },
|
|
14
14
|
{ name: "create", short: "Create a new pitch", path: "shapeup pitches create \"Title\" --stream \"Name\"" },
|
|
15
|
+
{ name: "update", short: "Update a pitch's title, status, appetite, stream, or cycle", path: "shapeup pitches update <id> --status shaped" },
|
|
16
|
+
{ name: "extend", short: "Link a pitch as a continuation of a predecessor", path: "shapeup pitches extend <id> --predecessor <id>" },
|
|
17
|
+
{ name: "detach", short: "Remove a pitch's predecessor link", path: "shapeup pitches detach <id>" },
|
|
18
|
+
{ name: "delete", short: "Delete a pitch (must have no scopes)", path: "shapeup pitches delete <id>" },
|
|
15
19
|
{ name: "help", short: "Show usage", path: "shapeup pitches help" }
|
|
16
20
|
],
|
|
17
21
|
flags: [
|
|
18
|
-
{ name: "status", type: "string", usage: "Filter by status: idea, framed, shaped" },
|
|
19
|
-
{ name: "cycle", type: "string", usage: "Filter by cycle ID" },
|
|
22
|
+
{ name: "status", type: "string", usage: "Filter by, or set, status: idea, framed, shaped" },
|
|
23
|
+
{ name: "cycle", type: "string", usage: "Filter by cycle ID (list), or assign to cycle ID (update)" },
|
|
20
24
|
{ name: "tag", type: "string", usage: "Filter by tag name" },
|
|
21
25
|
{ name: "limit", type: "integer", usage: "Limit number of results" },
|
|
22
|
-
{ name: "stream", type: "string", usage: "Stream name or ID (for create)" },
|
|
23
|
-
{ name: "appetite", type: "string", usage: "Appetite: unknown, small_batch, big_batch (
|
|
26
|
+
{ name: "stream", type: "string", usage: "Stream name or ID (for create/update)" },
|
|
27
|
+
{ name: "appetite", type: "string", usage: "Appetite: unknown, small_batch, big_batch (create default: big_batch)" },
|
|
24
28
|
{ name: "cycle-id", type: "string", usage: "Assign to cycle ID (for create)" },
|
|
29
|
+
{ name: "title", type: "string", usage: "New title (for update)" },
|
|
30
|
+
{ name: "content", type: "string", usage: "New description (for update)" },
|
|
31
|
+
{ name: "predecessor", type: "string", usage: "Predecessor pitch ID (for extend)" },
|
|
25
32
|
{ name: "no-comments", type: "bool", usage: "Hide embedded comments on show (default: show)" },
|
|
26
33
|
{ name: "comments-limit", type: "integer", usage: "Max comments to embed on show (default: 10, max: 50)" }
|
|
27
34
|
],
|
|
@@ -33,7 +40,13 @@ module ShapeupCli
|
|
|
33
40
|
"shapeup pitch 42",
|
|
34
41
|
"shapeup pitch 42 --json",
|
|
35
42
|
"shapeup pitches create \"Redesign Search\" --stream \"Platform\"",
|
|
36
|
-
"shapeup pitches create \"Auth Overhaul\" --stream \"Platform\" --appetite small_batch"
|
|
43
|
+
"shapeup pitches create \"Auth Overhaul\" --stream \"Platform\" --appetite small_batch",
|
|
44
|
+
"shapeup pitches update 42 --status shaped",
|
|
45
|
+
"shapeup pitches update 42 --title \"Redesign Search v2\" --appetite small_batch",
|
|
46
|
+
"shapeup pitches update 42 --cycle 5",
|
|
47
|
+
"shapeup pitches extend 42 --predecessor 30",
|
|
48
|
+
"shapeup pitches detach 42",
|
|
49
|
+
"shapeup pitches delete 42"
|
|
37
50
|
]
|
|
38
51
|
}
|
|
39
52
|
end
|
|
@@ -44,6 +57,10 @@ module ShapeupCli
|
|
|
44
57
|
case subcommand
|
|
45
58
|
when "show" then show
|
|
46
59
|
when "create" then create
|
|
60
|
+
when "update" then update
|
|
61
|
+
when "extend" then extend_pitch
|
|
62
|
+
when "detach" then detach
|
|
63
|
+
when "delete" then delete
|
|
47
64
|
when "list", nil then list
|
|
48
65
|
when "help" then help
|
|
49
66
|
else
|
|
@@ -140,6 +157,68 @@ module ShapeupCli
|
|
|
140
157
|
]
|
|
141
158
|
end
|
|
142
159
|
|
|
160
|
+
def update
|
|
161
|
+
id = positional_arg(1) || abort("Usage: shapeup pitches update <id> [--title \"...\"] [--status idea|framed|shaped] [--appetite ...] [--stream \"Name\"] [--content \"...\"] [--cycle <id>]")
|
|
162
|
+
|
|
163
|
+
args = { package: id.to_s }
|
|
164
|
+
args[:title] = extract_option("--title") if @remaining.include?("--title")
|
|
165
|
+
args[:status] = extract_option("--status") if @remaining.include?("--status")
|
|
166
|
+
args[:appetite] = extract_option("--appetite") if @remaining.include?("--appetite")
|
|
167
|
+
args[:stream] = extract_option("--stream") if @remaining.include?("--stream")
|
|
168
|
+
args[:content] = extract_option("--content") if @remaining.include?("--content")
|
|
169
|
+
args[:cycle] = extract_option("--cycle") if @remaining.include?("--cycle")
|
|
170
|
+
|
|
171
|
+
abort("Nothing to update. Pass at least one of --title, --status, --appetite, --stream, --content, --cycle") if args.size == 1
|
|
172
|
+
|
|
173
|
+
result = call_tool("update_package", **args)
|
|
174
|
+
|
|
175
|
+
render result,
|
|
176
|
+
summary: "Pitch ##{id} updated",
|
|
177
|
+
breadcrumbs: [
|
|
178
|
+
{ cmd: "shapeup pitch #{id}", description: "View pitch details" }
|
|
179
|
+
]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def extend_pitch
|
|
183
|
+
id = positional_arg(1) || abort("Usage: shapeup pitches extend <id> --predecessor <id>")
|
|
184
|
+
predecessor = extract_option("--predecessor") || abort("Usage: shapeup pitches extend <id> --predecessor <id>")
|
|
185
|
+
|
|
186
|
+
result = call_tool("extend_package", package: id.to_s, predecessor: predecessor.to_s)
|
|
187
|
+
|
|
188
|
+
render result,
|
|
189
|
+
summary: "Pitch ##{id} now extends ##{predecessor}",
|
|
190
|
+
breadcrumbs: [
|
|
191
|
+
{ cmd: "shapeup pitch #{id}", description: "View pitch details" },
|
|
192
|
+
{ cmd: "shapeup pitches detach #{id}", description: "Remove the predecessor link" }
|
|
193
|
+
]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def detach
|
|
197
|
+
id = positional_arg(1) || abort("Usage: shapeup pitches detach <id>")
|
|
198
|
+
|
|
199
|
+
result = call_tool("detach_package", package: id.to_s)
|
|
200
|
+
|
|
201
|
+
render result,
|
|
202
|
+
summary: "Pitch ##{id} detached from its predecessor",
|
|
203
|
+
breadcrumbs: [
|
|
204
|
+
{ cmd: "shapeup pitch #{id}", description: "View pitch details" }
|
|
205
|
+
]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def delete
|
|
209
|
+
yes = assume_yes?
|
|
210
|
+
id = positional_arg(1) || abort("Usage: shapeup pitches delete <id> [--yes]")
|
|
211
|
+
confirm_destructive!("Delete pitch ##{id}", yes)
|
|
212
|
+
|
|
213
|
+
result = call_tool("delete_package", package: id.to_s)
|
|
214
|
+
|
|
215
|
+
render result,
|
|
216
|
+
summary: "Pitch ##{id} deleted",
|
|
217
|
+
breadcrumbs: [
|
|
218
|
+
{ cmd: "shapeup pitches list", description: "List remaining pitches" }
|
|
219
|
+
]
|
|
220
|
+
end
|
|
221
|
+
|
|
143
222
|
def help
|
|
144
223
|
puts <<~HELP
|
|
145
224
|
Usage: shapeup pitches <subcommand> [options]
|
|
@@ -12,7 +12,8 @@ module ShapeupCli
|
|
|
12
12
|
{ name: "list", short: "List scopes for a pitch", path: "shapeup scopes list --pitch <id>" },
|
|
13
13
|
{ name: "create", short: "Create a new scope", path: "shapeup scopes create --pitch <id> \"Title\"" },
|
|
14
14
|
{ name: "update", short: "Update scope title or color", path: "shapeup scopes update <id> --title \"New\"" },
|
|
15
|
-
{ name: "position", short: "Update hill chart position (0-100)", path: "shapeup scopes position <id> <position>" }
|
|
15
|
+
{ name: "position", short: "Update hill chart position (0-100)", path: "shapeup scopes position <id> <position>" },
|
|
16
|
+
{ name: "delete", short: "Delete a scope (must have no tasks or comments)", path: "shapeup scopes delete <id>" }
|
|
16
17
|
],
|
|
17
18
|
flags: [
|
|
18
19
|
{ name: "pitch", type: "string", usage: "Pitch ID (required for list and create)" },
|
|
@@ -23,7 +24,8 @@ module ShapeupCli
|
|
|
23
24
|
"shapeup scopes list --pitch 42",
|
|
24
25
|
"shapeup scopes create --pitch 42 \"User onboarding\"",
|
|
25
26
|
"shapeup scopes update 7 --title \"Revised onboarding\"",
|
|
26
|
-
"shapeup scopes position 7 50"
|
|
27
|
+
"shapeup scopes position 7 50",
|
|
28
|
+
"shapeup scopes delete 7"
|
|
27
29
|
]
|
|
28
30
|
}
|
|
29
31
|
end
|
|
@@ -35,6 +37,7 @@ module ShapeupCli
|
|
|
35
37
|
when "create" then create
|
|
36
38
|
when "update" then update
|
|
37
39
|
when "position" then position
|
|
40
|
+
when "delete" then delete
|
|
38
41
|
when "list", nil then list
|
|
39
42
|
else list
|
|
40
43
|
end
|
|
@@ -91,6 +94,20 @@ module ShapeupCli
|
|
|
91
94
|
|
|
92
95
|
render result, summary: "Scope ##{scope_id} updated"
|
|
93
96
|
end
|
|
97
|
+
|
|
98
|
+
def delete
|
|
99
|
+
yes = assume_yes?
|
|
100
|
+
scope_id = positional_arg(1) || abort("Usage: shapeup scopes delete <id> [--yes]")
|
|
101
|
+
confirm_destructive!("Delete scope ##{scope_id}", yes)
|
|
102
|
+
|
|
103
|
+
result = call_tool("delete_scope", scope: scope_id.to_s)
|
|
104
|
+
|
|
105
|
+
render result,
|
|
106
|
+
summary: "Scope ##{scope_id} deleted",
|
|
107
|
+
breadcrumbs: [
|
|
108
|
+
{ cmd: "shapeup scopes list --pitch <id>", description: "List remaining scopes" }
|
|
109
|
+
]
|
|
110
|
+
end
|
|
94
111
|
end
|
|
95
112
|
end
|
|
96
113
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShapeupCli
|
|
4
|
+
module Commands
|
|
5
|
+
class Streams < Base
|
|
6
|
+
def self.metadata
|
|
7
|
+
{
|
|
8
|
+
command: "streams",
|
|
9
|
+
path: "shapeup streams",
|
|
10
|
+
short: "List and show streams (product areas)",
|
|
11
|
+
subcommands: [
|
|
12
|
+
{ name: "list", short: "List streams (default)", path: "shapeup streams list" },
|
|
13
|
+
{ name: "show", short: "Show stream details with pitches and issue counts", path: "shapeup streams show <id>" }
|
|
14
|
+
],
|
|
15
|
+
flags: [
|
|
16
|
+
{ name: "all", type: "bool", usage: "Include archived streams (for list)" }
|
|
17
|
+
],
|
|
18
|
+
examples: [
|
|
19
|
+
"shapeup streams",
|
|
20
|
+
"shapeup streams --all --json",
|
|
21
|
+
"shapeup streams show 3"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute
|
|
27
|
+
subcommand = positional_arg(0)
|
|
28
|
+
|
|
29
|
+
case subcommand
|
|
30
|
+
when "show" then show
|
|
31
|
+
when "list", nil then list
|
|
32
|
+
else
|
|
33
|
+
subcommand.match?(/\A\d+\z/) ? show(subcommand) : list
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def list
|
|
39
|
+
args = {}
|
|
40
|
+
args[:include_archived] = true if consume_flag("--all")
|
|
41
|
+
|
|
42
|
+
result = call_tool("list_streams", **args)
|
|
43
|
+
|
|
44
|
+
render result,
|
|
45
|
+
summary: "Streams",
|
|
46
|
+
breadcrumbs: [
|
|
47
|
+
{ cmd: "shapeup streams show <id>", description: "View stream details" },
|
|
48
|
+
{ cmd: "shapeup pitches create \"Title\" --stream \"Name\"", description: "Create a pitch in a stream" }
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show(id = nil)
|
|
53
|
+
id ||= positional_arg(1) || abort("Usage: shapeup streams show <id>")
|
|
54
|
+
|
|
55
|
+
result = call_tool("show_stream", stream: id.to_s)
|
|
56
|
+
|
|
57
|
+
render result,
|
|
58
|
+
summary: "Stream ##{id}",
|
|
59
|
+
breadcrumbs: [
|
|
60
|
+
{ cmd: "shapeup pitches list --json", description: "List pitches" },
|
|
61
|
+
{ cmd: "shapeup issues --stream \"Name\"", description: "List issues in this stream" }
|
|
62
|
+
]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -8,23 +8,32 @@ module ShapeupCli
|
|
|
8
8
|
command: "tasks",
|
|
9
9
|
path: "shapeup tasks",
|
|
10
10
|
short: "Manage tasks within scopes and pitches",
|
|
11
|
-
aliases: { "todo" => "tasks create", "done" => "tasks complete" },
|
|
11
|
+
aliases: { "todo" => "tasks create", "done" => "tasks complete", "undone" => "tasks uncomplete" },
|
|
12
12
|
subcommands: [
|
|
13
13
|
{ name: "list", short: "List tasks", path: "shapeup tasks list" },
|
|
14
14
|
{ name: "create", short: "Create a task", path: "shapeup todo \"Description\" --pitch <id>" },
|
|
15
|
-
{ name: "complete", short: "Mark task(s) as complete", path: "shapeup done <id> [<id>...]" }
|
|
15
|
+
{ name: "complete", short: "Mark task(s) as complete", path: "shapeup done <id> [<id>...]" },
|
|
16
|
+
{ name: "uncomplete", short: "Mark task as incomplete", path: "shapeup undone <id>" },
|
|
17
|
+
{ name: "update", short: "Edit a task's description or move it to a scope", path: "shapeup tasks update <id> --description \"New\"" },
|
|
18
|
+
{ name: "delete", short: "Delete a task", path: "shapeup tasks delete <id>" }
|
|
16
19
|
],
|
|
17
20
|
flags: [
|
|
18
21
|
{ name: "pitch", type: "string", usage: "Pitch ID (required for create)" },
|
|
19
|
-
{ name: "scope", type: "string", usage: "Scope ID (
|
|
20
|
-
{ name: "assignee", type: "string", usage: "User ID or 'me' (for list)" }
|
|
22
|
+
{ name: "scope", type: "string", usage: "Scope ID (filter, create target, or update target; use 'none' to unmap)" },
|
|
23
|
+
{ name: "assignee", type: "string", usage: "User ID or 'me' (for list)" },
|
|
24
|
+
{ name: "description", type: "string", usage: "New description (for update)" }
|
|
21
25
|
],
|
|
22
26
|
examples: [
|
|
23
27
|
"shapeup tasks list --pitch 42",
|
|
24
28
|
"shapeup tasks list --assignee me",
|
|
25
29
|
"shapeup todo \"Fix login bug\" --pitch 42 --scope 7",
|
|
26
30
|
"shapeup done 123",
|
|
27
|
-
"shapeup done 123 124 125"
|
|
31
|
+
"shapeup done 123 124 125",
|
|
32
|
+
"shapeup undone 123",
|
|
33
|
+
"shapeup tasks update 123 --description \"Fix login + signup bug\"",
|
|
34
|
+
"shapeup tasks update 123 --scope 7",
|
|
35
|
+
"shapeup tasks update 123 --scope none",
|
|
36
|
+
"shapeup tasks delete 123"
|
|
28
37
|
]
|
|
29
38
|
}
|
|
30
39
|
end
|
|
@@ -33,9 +42,12 @@ module ShapeupCli
|
|
|
33
42
|
subcommand = positional_arg(0)
|
|
34
43
|
|
|
35
44
|
case subcommand
|
|
36
|
-
when "create"
|
|
37
|
-
when "complete"
|
|
38
|
-
when "
|
|
45
|
+
when "create" then create
|
|
46
|
+
when "complete" then complete
|
|
47
|
+
when "uncomplete" then uncomplete
|
|
48
|
+
when "update" then update
|
|
49
|
+
when "delete" then delete
|
|
50
|
+
when "list", nil then list
|
|
39
51
|
else list
|
|
40
52
|
end
|
|
41
53
|
end
|
|
@@ -91,10 +103,56 @@ module ShapeupCli
|
|
|
91
103
|
render result,
|
|
92
104
|
summary: "Task ##{id} completed",
|
|
93
105
|
breadcrumbs: [
|
|
106
|
+
{ cmd: "shapeup undone #{id}", description: "Mark incomplete again" },
|
|
94
107
|
{ cmd: "shapeup me", description: "Show remaining work" }
|
|
95
108
|
]
|
|
96
109
|
end
|
|
97
110
|
end
|
|
111
|
+
|
|
112
|
+
def uncomplete
|
|
113
|
+
id = positional_arg(1) || abort("Usage: shapeup undone <id>")
|
|
114
|
+
|
|
115
|
+
result = call_tool("uncomplete_task", task: id.to_s)
|
|
116
|
+
|
|
117
|
+
render result,
|
|
118
|
+
summary: "Task ##{id} marked incomplete",
|
|
119
|
+
breadcrumbs: [
|
|
120
|
+
{ cmd: "shapeup done #{id}", description: "Complete it again" }
|
|
121
|
+
]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def update
|
|
125
|
+
id = positional_arg(1) || abort("Usage: shapeup tasks update <id> [--description \"New\"] [--scope <id>|none]")
|
|
126
|
+
description = extract_option("--description")
|
|
127
|
+
scope = extract_option("--scope")
|
|
128
|
+
abort("Nothing to update. Pass --description \"...\" and/or --scope <id>|none") unless description || scope
|
|
129
|
+
|
|
130
|
+
args = { task: id.to_s }
|
|
131
|
+
args[:description] = description if description
|
|
132
|
+
args[:scope] = scope if scope
|
|
133
|
+
|
|
134
|
+
result = call_tool("update_task", **args)
|
|
135
|
+
|
|
136
|
+
render result,
|
|
137
|
+
summary: "Task ##{id} updated",
|
|
138
|
+
breadcrumbs: [
|
|
139
|
+
{ cmd: "shapeup tasks list --pitch <id>", description: "List tasks" }
|
|
140
|
+
]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def delete
|
|
144
|
+
yes = assume_yes?
|
|
145
|
+
id = positional_arg(1) || abort("Usage: shapeup tasks delete <id> [--yes]")
|
|
146
|
+
confirm_destructive!("Delete task ##{id}", yes)
|
|
147
|
+
|
|
148
|
+
result = call_tool("delete_task", task: id.to_s)
|
|
149
|
+
|
|
150
|
+
render result,
|
|
151
|
+
summary: "Task ##{id} deleted",
|
|
152
|
+
breadcrumbs: [
|
|
153
|
+
{ cmd: "shapeup tasks list --pitch <id>", description: "List remaining tasks" }
|
|
154
|
+
]
|
|
155
|
+
end
|
|
98
156
|
end
|
|
99
157
|
end
|
|
100
158
|
end
|
data/lib/shapeup_cli/commands.rb
CHANGED
|
@@ -24,20 +24,35 @@ module ShapeupCli
|
|
|
24
24
|
pitches list List pitches (packages)
|
|
25
25
|
pitches show <id> Show pitch details with scopes and tasks
|
|
26
26
|
pitch <id> Shortcut for pitches show
|
|
27
|
+
pitches create "Title" --stream "Name" [--appetite small_batch] [--cycle-id <id>]
|
|
28
|
+
pitches update <id> [--title|--status|--appetite|--stream|--content|--cycle ...]
|
|
29
|
+
pitches extend <id> --predecessor <id> Continue a previous pitch
|
|
30
|
+
pitches detach <id> Remove the predecessor link
|
|
31
|
+
pitches delete <id> Delete a pitch (asks to confirm; --yes to skip)
|
|
27
32
|
|
|
28
33
|
Cycles:
|
|
29
34
|
cycles List all cycles
|
|
30
35
|
cycle show <id> Show cycle details with pitches and progress
|
|
31
36
|
|
|
37
|
+
Streams:
|
|
38
|
+
streams List streams (product areas)
|
|
39
|
+
streams --all Include archived streams
|
|
40
|
+
streams show <id> Show stream details
|
|
41
|
+
|
|
32
42
|
Scopes:
|
|
33
43
|
scopes list --pitch <id> List scopes for a pitch
|
|
34
44
|
scopes create --pitch <id> "Title"
|
|
35
45
|
scopes update <id> --title "New title"
|
|
46
|
+
scopes position <id> <0-100> Update hill chart position
|
|
47
|
+
scopes delete <id> Delete a scope (asks to confirm; --yes to skip)
|
|
36
48
|
|
|
37
49
|
Tasks:
|
|
38
50
|
tasks list --scope <id> List tasks for a scope
|
|
39
51
|
todo "Description" --pitch <id> [--scope <id>]
|
|
40
52
|
done <id> [<id>...] Mark task(s) as complete
|
|
53
|
+
undone <id> Mark a task as incomplete
|
|
54
|
+
tasks update <id> [--description "New"] [--scope <id>|none]
|
|
55
|
+
tasks delete <id> Delete a task (asks to confirm; --yes to skip)
|
|
41
56
|
|
|
42
57
|
Issues:
|
|
43
58
|
issues List open issues
|
|
@@ -61,7 +76,7 @@ module ShapeupCli
|
|
|
61
76
|
issues watch <id> Watch an issue
|
|
62
77
|
issues unwatch <id> Stop watching
|
|
63
78
|
watching List issues you are watching
|
|
64
|
-
issues delete <id> Delete an issue
|
|
79
|
+
issues delete <id> Delete an issue (asks to confirm; --yes to skip)
|
|
65
80
|
issues convert <id> Convert issue to a new pitch
|
|
66
81
|
issues add-to-pitch <id> --pitch <pid>
|
|
67
82
|
Fold issue into an existing pitch
|
|
@@ -71,6 +86,8 @@ module ShapeupCli
|
|
|
71
86
|
comments list --pitch <id> List comments on a pitch
|
|
72
87
|
comments add --issue <id> "Text" Add a comment to an issue
|
|
73
88
|
comments add --pitch <id> "Text" Add a comment to a pitch
|
|
89
|
+
comments edit <comment_id> "Text" Edit your own comment
|
|
90
|
+
comments remove <comment_id> Delete your own comment (asks to confirm)
|
|
74
91
|
|
|
75
92
|
Checklist:
|
|
76
93
|
checklist --pitch <id> List the checklist on a pitch
|
|
@@ -116,6 +133,8 @@ module ShapeupCli
|
|
|
116
133
|
Flags:
|
|
117
134
|
--org <id|name> Override default organisation
|
|
118
135
|
--host <url> Override ShapeUp host (default: https://shapeup.cc)
|
|
136
|
+
--yes, -y Skip the confirmation prompt on delete commands
|
|
137
|
+
(required when deleting non-interactively)
|
|
119
138
|
|
|
120
139
|
Environment variables:
|
|
121
140
|
SHAPEUP_TOKEN Bearer token (skips OAuth, for CI/scripts)
|
|
@@ -139,18 +158,20 @@ module ShapeupCli
|
|
|
139
158
|
logout Clear all credentials
|
|
140
159
|
auth Manage profiles (status, list, switch, remove)
|
|
141
160
|
orgs List organisations
|
|
142
|
-
pitches
|
|
161
|
+
pitches Manage pitches (list, show, create, update, extend, detach, delete)
|
|
143
162
|
pitch Show a pitch (shortcut)
|
|
144
163
|
cycles List cycles
|
|
145
164
|
cycle Show cycle details (list, show)
|
|
146
|
-
|
|
147
|
-
|
|
165
|
+
streams List/show streams (list, show)
|
|
166
|
+
scopes Manage scopes (list, create, update, position, delete)
|
|
167
|
+
tasks Manage tasks (list, create, complete, uncomplete, update, delete)
|
|
148
168
|
todo Create a task (shortcut)
|
|
149
169
|
done Complete task(s) (shortcut)
|
|
150
|
-
|
|
170
|
+
undone Mark a task incomplete (shortcut)
|
|
171
|
+
issues Manage issues (list, show, create, move, icebox, watch, convert, add-to-pitch, delete)
|
|
151
172
|
issue Show an issue (shortcut)
|
|
152
173
|
watching List watched issues (shortcut)
|
|
153
|
-
comments
|
|
174
|
+
comments Manage comments (list, add, edit, remove)
|
|
154
175
|
checklist Manage checklist items on pitches/issues (list, add, tick, untick, edit, remove)
|
|
155
176
|
tags List/add/remove tags on pitches and issues
|
|
156
177
|
my-work / me Show my assigned work
|
|
@@ -171,6 +192,7 @@ require_relative "commands/logout"
|
|
|
171
192
|
require_relative "commands/orgs"
|
|
172
193
|
require_relative "commands/pitches"
|
|
173
194
|
require_relative "commands/cycle"
|
|
195
|
+
require_relative "commands/streams"
|
|
174
196
|
require_relative "commands/scopes"
|
|
175
197
|
require_relative "commands/tasks"
|
|
176
198
|
require_relative "commands/issues"
|
data/lib/shapeup_cli/output.rb
CHANGED
|
@@ -92,7 +92,7 @@ module ShapeupCli
|
|
|
92
92
|
when Hash
|
|
93
93
|
render_markdown_hash(data)
|
|
94
94
|
else
|
|
95
|
-
puts data
|
|
95
|
+
puts data
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -106,7 +106,7 @@ module ShapeupCli
|
|
|
106
106
|
when Hash
|
|
107
107
|
render_styled_hash(data)
|
|
108
108
|
else
|
|
109
|
-
puts data
|
|
109
|
+
puts data
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
if breadcrumbs.any?
|
|
@@ -177,7 +177,7 @@ module ShapeupCli
|
|
|
177
177
|
when Hash
|
|
178
178
|
id = value["id"]
|
|
179
179
|
label = value["title"] || value["name"] || value["description"]
|
|
180
|
-
[id, label].compact.join(" ")
|
|
180
|
+
[ id, label ].compact.join(" ")
|
|
181
181
|
else
|
|
182
182
|
value.to_s
|
|
183
183
|
end
|
data/lib/shapeup_cli.rb
CHANGED
|
@@ -16,7 +16,7 @@ require_relative "shapeup_cli/output"
|
|
|
16
16
|
require_relative "shapeup_cli/commands"
|
|
17
17
|
|
|
18
18
|
module ShapeupCli
|
|
19
|
-
VERSION = "0.
|
|
19
|
+
VERSION = "0.4.0"
|
|
20
20
|
DEFAULT_HOST = "https://shapeup.cc"
|
|
21
21
|
|
|
22
22
|
# Exit codes
|
|
@@ -33,6 +33,7 @@ module ShapeupCli
|
|
|
33
33
|
"orgs" => Commands::Orgs,
|
|
34
34
|
"pitches" => Commands::Pitches,
|
|
35
35
|
"cycle" => Commands::Cycle,
|
|
36
|
+
"streams" => Commands::Streams,
|
|
36
37
|
"scopes" => Commands::Scopes,
|
|
37
38
|
"tasks" => Commands::Tasks,
|
|
38
39
|
"issues" => Commands::Issues,
|
|
@@ -68,16 +69,18 @@ module ShapeupCli
|
|
|
68
69
|
when "auth" then Commands::Auth.run(args)
|
|
69
70
|
when "orgs" then Commands::Orgs.run(args)
|
|
70
71
|
when "pitches" then Commands::Pitches.run(args)
|
|
71
|
-
when "pitch" then Commands::Pitches.run(["show"] + args)
|
|
72
|
+
when "pitch" then Commands::Pitches.run([ "show" ] + args)
|
|
72
73
|
when "cycle" then Commands::Cycle.run(args)
|
|
73
|
-
when "cycles" then Commands::Cycle.run(["list"] + args)
|
|
74
|
+
when "cycles" then Commands::Cycle.run([ "list" ] + args)
|
|
75
|
+
when "streams" then Commands::Streams.run(args)
|
|
74
76
|
when "scopes" then Commands::Scopes.run(args)
|
|
75
77
|
when "tasks" then Commands::Tasks.run(args)
|
|
76
|
-
when "todo" then Commands::Tasks.run(["create"] + args)
|
|
77
|
-
when "done" then Commands::Tasks.run(["complete"] + args)
|
|
78
|
+
when "todo" then Commands::Tasks.run([ "create" ] + args)
|
|
79
|
+
when "done" then Commands::Tasks.run([ "complete" ] + args)
|
|
80
|
+
when "undone" then Commands::Tasks.run([ "uncomplete" ] + args)
|
|
78
81
|
when "issues" then Commands::Issues.run(args)
|
|
79
|
-
when "issue" then Commands::Issues.run(["show"] + args)
|
|
80
|
-
when "watching" then Commands::Issues.run(["watching"] + args)
|
|
82
|
+
when "issue" then Commands::Issues.run([ "show" ] + args)
|
|
83
|
+
when "watching" then Commands::Issues.run([ "watching" ] + args)
|
|
81
84
|
when "comments" then Commands::Comments.run(args)
|
|
82
85
|
when "checklist" then Commands::Checklist.run(args)
|
|
83
86
|
when "tags" then Commands::Tags.run(args)
|
|
@@ -126,6 +129,7 @@ module ShapeupCli
|
|
|
126
129
|
"cycles" => "cycle list",
|
|
127
130
|
"todo \"...\"" => "tasks create \"...\"",
|
|
128
131
|
"done <id>" => "tasks complete <id>",
|
|
132
|
+
"undone <id>" => "tasks uncomplete <id>",
|
|
129
133
|
"issue <id>" => "issues show <id>",
|
|
130
134
|
"watching" => "issues watching",
|
|
131
135
|
"me" => "my-work"
|
data/skills/shapeup/SKILL.md
CHANGED
|
@@ -78,6 +78,7 @@ Manage pitches, scopes, tasks, issues, and cycles via the ShapeUp CLI. Columns a
|
|
|
78
78
|
5. **"Pitch" = "Package" in code** — users say "pitch", the API uses "package". The CLI uses "pitch" everywhere.
|
|
79
79
|
6. **Use 'me' and 'none'** — `--assignee me` for current user, `--assignee none` for unassigned items.
|
|
80
80
|
7. **Check exit codes** — 0=OK, 2=not found, 3=auth error, 4=permission denied, 5=API error. Branch on exit code without parsing error text.
|
|
81
|
+
8. **Deletes need confirmation** — `delete`/`remove` commands (pitches, scopes, tasks, issues, comments) prompt `[y/N]` interactively and **refuse when run non-interactively** unless you pass `--yes` (or `-y`). As an agent you have no TTY, so add `--yes` only after you have confirmed the deletion is intended.
|
|
81
82
|
|
|
82
83
|
### Output Modes
|
|
83
84
|
|
|
@@ -140,6 +141,8 @@ Manage pitches, scopes, tasks, issues, and cycles via the ShapeUp CLI. Columns a
|
|
|
140
141
|
| List comments on pitch | `shapeup comments list --pitch <id> --json` |
|
|
141
142
|
| Add comment to issue | `shapeup comments add --issue <id> "Comment text"` |
|
|
142
143
|
| Add comment to pitch | `shapeup comments add --pitch <id> "Comment text"` |
|
|
144
|
+
| Edit your comment | `shapeup comments edit <comment_id> "New text"` |
|
|
145
|
+
| Delete your comment | `shapeup comments remove <comment_id> --yes` |
|
|
143
146
|
| **Checklist** | |
|
|
144
147
|
| List checklist on pitch | `shapeup checklist --pitch <id>` |
|
|
145
148
|
| List checklist on issue | `shapeup checklist --issue <id>` |
|
|
@@ -159,17 +162,29 @@ Manage pitches, scopes, tasks, issues, and cycles via the ShapeUp CLI. Columns a
|
|
|
159
162
|
| Show pitch detail | `shapeup pitch <id> --json` |
|
|
160
163
|
| Create pitch | `shapeup pitches create "Title" --stream "Name"` |
|
|
161
164
|
| Create with appetite | `shapeup pitches create "Title" --stream "Name" --appetite small_batch` |
|
|
165
|
+
| Update pitch | `shapeup pitches update <id> --status shaped` |
|
|
166
|
+
| Extend a pitch | `shapeup pitches extend <id> --predecessor <id>` |
|
|
167
|
+
| Detach predecessor | `shapeup pitches detach <id>` |
|
|
168
|
+
| Delete pitch | `shapeup pitches delete <id> --yes` |
|
|
162
169
|
| **Cycles** | |
|
|
163
170
|
| List cycles | `shapeup cycles --json` |
|
|
164
171
|
| Active cycles | `shapeup cycles --status active --json` |
|
|
165
172
|
| Show cycle | `shapeup cycle show <id> --json` |
|
|
173
|
+
| **Streams** | |
|
|
174
|
+
| List streams | `shapeup streams --json` |
|
|
175
|
+
| Include archived | `shapeup streams --all --json` |
|
|
176
|
+
| Show stream | `shapeup streams show <id> --json` |
|
|
166
177
|
| **Scopes & Tasks** | |
|
|
167
178
|
| List scopes | `shapeup scopes list --pitch <id> --json` |
|
|
168
179
|
| Create scope | `shapeup scopes create --pitch <id> "Title"` |
|
|
169
180
|
| Update hill position | `shapeup scopes position <id> <0-100>` |
|
|
181
|
+
| Delete scope | `shapeup scopes delete <id> --yes` |
|
|
170
182
|
| List tasks | `shapeup tasks list --pitch <id> --json` |
|
|
171
183
|
| Create task | `shapeup todo "Description" --pitch <id>` |
|
|
172
184
|
| Complete task(s) | `shapeup done <id> [<id>...]` |
|
|
185
|
+
| Uncomplete task | `shapeup undone <id>` |
|
|
186
|
+
| Update task | `shapeup tasks update <id> --description "New"` |
|
|
187
|
+
| Delete task | `shapeup tasks delete <id> --yes` |
|
|
173
188
|
| **My Work** | |
|
|
174
189
|
| All my assignments | `shapeup me --json` |
|
|
175
190
|
| My work (alias) | `shapeup my-work --json` |
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shapeup-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ShapeUp
|
|
@@ -40,6 +40,7 @@ files:
|
|
|
40
40
|
- lib/shapeup_cli/commands/scopes.rb
|
|
41
41
|
- lib/shapeup_cli/commands/search.rb
|
|
42
42
|
- lib/shapeup_cli/commands/setup.rb
|
|
43
|
+
- lib/shapeup_cli/commands/streams.rb
|
|
43
44
|
- lib/shapeup_cli/commands/tags.rb
|
|
44
45
|
- lib/shapeup_cli/commands/tasks.rb
|
|
45
46
|
- lib/shapeup_cli/config.rb
|
|
@@ -66,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
66
67
|
- !ruby/object:Gem::Version
|
|
67
68
|
version: '0'
|
|
68
69
|
requirements: []
|
|
69
|
-
rubygems_version: 4.0.
|
|
70
|
+
rubygems_version: 4.0.10
|
|
70
71
|
specification_version: 4
|
|
71
72
|
summary: ShapeUp CLI — manage pitches, scopes, tasks, and cycles from the terminal
|
|
72
73
|
test_files: []
|