shapeup-cli 0.8.0 → 0.9.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecb0aff2791935bf727f5afbb283ec92bb181325bb57b8ba0d6778d05b18db26
|
|
4
|
+
data.tar.gz: 8f938f4be9b1dafc98c44916bcf539b936ba89fa0ca9c245cf78b1531c6a64c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f337c2f8aade0196c9cdaff8f48d7e10a4216736844e7b2381aa348dcc0855a12d8a0d3ee557fee7778599f1ab8bb15399676afcf87c8150b446e54e5328d323
|
|
7
|
+
data.tar.gz: 517305b0931a662d4b3b8a4cfb2910a3a879b64bfce4d303edaf7da7cf390a6f0c2c8f1155ab9f2f0261ec64c6ef69574fed070f564c0ad0b9f8b9ebe820149f
|
|
@@ -100,6 +100,14 @@ module ShapeupCli
|
|
|
100
100
|
value
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
def extract_options(flag)
|
|
104
|
+
values = []
|
|
105
|
+
while (value = extract_option(flag))
|
|
106
|
+
values << value
|
|
107
|
+
end
|
|
108
|
+
values
|
|
109
|
+
end
|
|
110
|
+
|
|
103
111
|
# Get the first positional arg (not a flag)
|
|
104
112
|
def positional_arg(position = 0)
|
|
105
113
|
positionals = @remaining.reject { |a| a.start_with?("--") }
|
|
@@ -20,7 +20,8 @@ module ShapeupCli
|
|
|
20
20
|
{ name: "pitch", type: "string", usage: "Pitch ID" },
|
|
21
21
|
{ name: "scope", type: "string", usage: "Scope ID" },
|
|
22
22
|
{ name: "task", type: "string", usage: "Task ID" },
|
|
23
|
-
{ name: "parent", type: "string", usage: "Comment ID to reply under" }
|
|
23
|
+
{ name: "parent", type: "string", usage: "Comment ID to reply under" },
|
|
24
|
+
{ name: "attach", type: "string", usage: "Image to show under the comment (repeatable). Local  images in the text are attached too" }
|
|
24
25
|
],
|
|
25
26
|
examples: [
|
|
26
27
|
"shapeup comments list --issue 42",
|
|
@@ -62,10 +63,15 @@ module ShapeupCli
|
|
|
62
63
|
def add
|
|
63
64
|
type, id = resolve_commentable
|
|
64
65
|
parent = extract_option("--parent")
|
|
65
|
-
|
|
66
|
+
attach_paths = extract_options("--attach")
|
|
67
|
+
text = positional_arg(1) || abort('Usage: shapeup comments add --issue <id> [--parent <comment_id>] [--attach <image>] "Comment text"')
|
|
68
|
+
|
|
69
|
+
text, images = local_images_in(text)
|
|
70
|
+
images += attach_paths.map { |path| [ path, nil ] }
|
|
66
71
|
|
|
67
72
|
arguments = { commentable_type: type, commentable_id: id.to_s, content: text }
|
|
68
73
|
arguments[:parent_id] = parent.to_s if parent
|
|
74
|
+
arguments[:attachments] = images.map { |path, alt| upload_image(path, alt) } if images.any?
|
|
69
75
|
result = call_tool("create_comment", **arguments)
|
|
70
76
|
|
|
71
77
|
render result,
|
|
@@ -103,6 +109,35 @@ module ShapeupCli
|
|
|
103
109
|
render result, summary: "Reacted #{emoji} to comment ##{comment_id}"
|
|
104
110
|
end
|
|
105
111
|
|
|
112
|
+
IMAGE_TYPES = { ".png" => "image/png", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".gif" => "image/gif", ".webp" => "image/webp" }.freeze
|
|
113
|
+
IMAGE_MAX_SIZE = 10 * 1024 * 1024
|
|
114
|
+
MARKDOWN_IMAGE = /!\[([^\]]*)\]\(([^)\s]+)\)\n?/
|
|
115
|
+
|
|
116
|
+
def local_images_in(text)
|
|
117
|
+
images = []
|
|
118
|
+
text = text.gsub(MARKDOWN_IMAGE) do |markdown|
|
|
119
|
+
alt, path = $1, $2
|
|
120
|
+
if path.match?(%r{\A[a-z][a-z0-9+.-]*://}i) || !File.file?(path)
|
|
121
|
+
markdown
|
|
122
|
+
else
|
|
123
|
+
images << [ path, alt ]
|
|
124
|
+
""
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
[ text.strip, images ]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def upload_image(path, alt)
|
|
131
|
+
abort "No such file: #{path}" unless File.file?(path)
|
|
132
|
+
content_type = IMAGE_TYPES[File.extname(path).downcase] or abort "Only PNG, JPEG, GIF and WebP images can be attached: #{path}"
|
|
133
|
+
abort "Images can be at most 10 MB: #{path}" if File.size(path) > IMAGE_MAX_SIZE
|
|
134
|
+
|
|
135
|
+
data = [ File.binread(path) ].pack("m0")
|
|
136
|
+
uploaded = Output.extract_data(call_tool("upload_attachment", filename: File.basename(path), content_type: content_type, data: data))
|
|
137
|
+
|
|
138
|
+
{ attachment: uploaded["attachment"], alt: alt.to_s.empty? ? File.basename(path, ".*").tr("-_", " ") : alt }
|
|
139
|
+
end
|
|
140
|
+
|
|
106
141
|
def resolve_commentable
|
|
107
142
|
issue = extract_option("--issue")
|
|
108
143
|
pitch = extract_option("--pitch")
|
|
@@ -121,7 +121,7 @@ module ShapeupCli
|
|
|
121
121
|
|
|
122
122
|
def profile_candidates
|
|
123
123
|
select_first [
|
|
124
|
-
{ source: "SHAPEUP_PROFILE env", value: ENV["SHAPEUP_PROFILE"] },
|
|
124
|
+
{ source: "--profile flag or SHAPEUP_PROFILE env", value: ENV["SHAPEUP_PROFILE"] },
|
|
125
125
|
{ source: "profiles.json default", value: Config.saved_default_profile }
|
|
126
126
|
]
|
|
127
127
|
end
|
|
@@ -130,6 +130,7 @@ module ShapeupCli
|
|
|
130
130
|
select_first [
|
|
131
131
|
{ source: "--org flag", value: @org_id },
|
|
132
132
|
{ source: "SHAPEUP_ORG env", value: ENV["SHAPEUP_ORG"] },
|
|
133
|
+
{ source: picked_profile_source, value: Config.chosen_profile_value("organisation_id") },
|
|
133
134
|
{ source: project_config_source, value: Config.project_config["organisation_id"] },
|
|
134
135
|
{ source: "~/.config/shapeup/config.json", value: Config.global_config["organisation_id"] },
|
|
135
136
|
{ source: "profile '#{Config.current_profile_name}'", value: Config.current_profile&.dig("organisation_id") }
|
|
@@ -139,6 +140,7 @@ module ShapeupCli
|
|
|
139
140
|
def host_candidates
|
|
140
141
|
select_first [
|
|
141
142
|
{ source: "SHAPEUP_HOST env", value: ENV["SHAPEUP_HOST"] },
|
|
143
|
+
{ source: picked_profile_source, value: Config.chosen_profile_value("host") },
|
|
142
144
|
{ source: project_config_source, value: Config.project_config["host"] },
|
|
143
145
|
{ source: "~/.config/shapeup/config.json", value: Config.global_config["host"] },
|
|
144
146
|
{ source: "profile '#{Config.current_profile_name}'", value: Config.current_profile&.dig("host") },
|
|
@@ -153,6 +155,10 @@ module ShapeupCli
|
|
|
153
155
|
]
|
|
154
156
|
end
|
|
155
157
|
|
|
158
|
+
def picked_profile_source
|
|
159
|
+
"profile picked with --profile (#{ENV["SHAPEUP_PROFILE"] || "none"})"
|
|
160
|
+
end
|
|
161
|
+
|
|
156
162
|
def project_config_source
|
|
157
163
|
Config.project_config_path || Config::PROJECT_CONFIG_NAME
|
|
158
164
|
end
|
data/lib/shapeup_cli/version.rb
CHANGED
data/skills/shapeup/SKILL.md
CHANGED
|
@@ -222,6 +222,7 @@ Branch on `retryable` to decide retry-vs-abandon; `false` means "no known reason
|
|
|
222
222
|
| Watch the inbox as JSON lines | `SHAPEUP_PROFILE=<agent> shapeup connect` |
|
|
223
223
|
| Reply under a comment | `shapeup comments add --issue <id> --parent <comment_id> "Reply"` |
|
|
224
224
|
| React to a comment | `shapeup comments react <comment_id> 👀` |
|
|
225
|
+
| Attach screenshots to a comment | `shapeup comments add --issue <id> "Text "` or `--attach shot.png` |
|
|
225
226
|
|
|
226
227
|
## Common Workflows
|
|
227
228
|
|
|
@@ -64,6 +64,23 @@ themselves.
|
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Always fill in the test command before asking, so the agent can check its work unattended.
|
|
68
|
+
Work out how this project runs its tests from what is in the directory, and list each way
|
|
69
|
+
you find:
|
|
70
|
+
|
|
71
|
+
| You find | Add |
|
|
72
|
+
|----------|-----|
|
|
73
|
+
| `bin/rails` | `"Bash(bin/rails test *)"` |
|
|
74
|
+
| `Rakefile` | `"Bash(bundle exec rake *)"` |
|
|
75
|
+
| a `test/` folder of Ruby files and no Rakefile | `"Bash(ruby test/*)"` |
|
|
76
|
+
| `package.json` with a `test` script | `"Bash(npm test *)"` (or the yarn/pnpm equivalent the lockfile shows) |
|
|
77
|
+
| `Makefile` with a test or check target | `"Bash(make test *)"` / `"Bash(make check *)"` |
|
|
78
|
+
| `pyproject.toml`, `pytest.ini` or `tests/` with Python | `"Bash(pytest *)"` |
|
|
79
|
+
| `go.mod` | `"Bash(go test *)"` |
|
|
80
|
+
| `Cargo.toml` | `"Bash(cargo test *)"` |
|
|
81
|
+
|
|
82
|
+
If you can't tell, ask the user how they run the tests rather than leaving it out.
|
|
83
|
+
|
|
67
84
|
Merge with what is there, never replace it, and make sure `.claude/settings.local.json` is in
|
|
68
85
|
`.gitignore`. If they say no, carry on: they will approve each command instead. If prompts keep
|
|
69
86
|
appearing after writing it, tell them a new session picks the rules up.
|
|
@@ -125,13 +142,20 @@ Give the subagent this, filled in:
|
|
|
125
142
|
> commit there. Never push, deploy, merge or delete branches unless the owner said so in this
|
|
126
143
|
> session.
|
|
127
144
|
> 4. If the ask is unclear or you are blocked, ask on the issue rather than guess.
|
|
128
|
-
> 5.
|
|
145
|
+
> 5. If the work changes anything people see, show it. Use whatever browser tool you have to take
|
|
146
|
+
> screenshots of each place it touches, before (from the main branch) and after (from yours),
|
|
147
|
+
> at the size people use it and on a phone when it matters. Save them as PNG files outside the
|
|
148
|
+
> repository, with names that say what they show.
|
|
149
|
+
> 6. Reply once, under the comment that asked:
|
|
129
150
|
> `shapeup --profile <profile> comments add --issue <issue.id> --parent <reply_to.comment_id> "<reply>"`
|
|
130
151
|
> (leave out `--parent` when `reply_to.comment_id` is null). Start with `@<requester.name>`.
|
|
131
152
|
> Say what changed, the branch and commit, how you checked it, and anything still open.
|
|
132
|
-
> Keep it short and plain.
|
|
133
|
-
>
|
|
134
|
-
>
|
|
153
|
+
> Keep it short and plain. Put each screenshot in the reply as a local Markdown image,
|
|
154
|
+
> ``: the CLI uploads it and
|
|
155
|
+
> the words in brackets become its description for people who can't see it, so write them
|
|
156
|
+
> properly. Mention anyone the requester asked you to hand over to.
|
|
157
|
+
> 7. Then take it out of the inbox: `shapeup --profile <profile> inbox done <id>`.
|
|
158
|
+
> 8. Report back in one line.
|
|
135
159
|
|
|
136
160
|
## Rules
|
|
137
161
|
|