slack-term 1.26.6 → 1.27.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.
- package/README.md +36 -1
- package/SKILL.md +39 -0
- package/dist/cli.js +98 -79
- package/package.json +5 -4
- package/ts/ask.ts +176 -0
- package/ts/cli.ts +761 -0
- package/ts/slack.ts +6 -0
package/README.md
CHANGED
|
@@ -93,6 +93,36 @@ slack delete "<permalink>"
|
|
|
93
93
|
slack react "#general:1700000000.000100" white_check_mark
|
|
94
94
|
slack react "<permalink>" eyes --remove # take a reaction back
|
|
95
95
|
|
|
96
|
+
# Ask a question with its choices pre-seeded as 1️⃣..🔟 reactions — answering is
|
|
97
|
+
# one tap on an existing pill, no emoji picker. Same two-step confirm gate as send.
|
|
98
|
+
slack ask "@bob" "本番に出してよい?" "出してよい" "待って"
|
|
99
|
+
# --wait blocks until answered and prints ONLY the answer on stdout, so it composes:
|
|
100
|
+
ANS=$(slack ask "@bob" "本番に出してよい?" "出してよい" "待って" --code=<code> --wait)
|
|
101
|
+
# exit 0 = answered, 2 = timed out (--timeout, default 3600s), 3 = transport failure.
|
|
102
|
+
#
|
|
103
|
+
# The question must say WHO may answer — only their reaction/reply is taken as the
|
|
104
|
+
# answer, so a bystander can't decide it for them. `ask` refuses to post otherwise.
|
|
105
|
+
slack ask "#eng" "@alice この PR 出してよい?" "出す" "待つ" # only alice's answer counts
|
|
106
|
+
slack ask "#eng" "@here 誰か見れる?" "見る" "あとで" # anyone in #eng; real broadcast
|
|
107
|
+
# (In a 1:1 DM the other party counts automatically — no tag needed.)
|
|
108
|
+
#
|
|
109
|
+
# With no choices the question asks for a free-text reply and the reply is the answer.
|
|
110
|
+
# In a DM a plain reply counts; in a channel only reactions and thread replies do.
|
|
111
|
+
# Once answered, the question is edited to "✅ …回答済み > <answer>" and the unpressed
|
|
112
|
+
# seeds are removed, leaving the chosen pill visible.
|
|
113
|
+
#
|
|
114
|
+
# WITHOUT --wait, stdout is the command that collects the answer later:
|
|
115
|
+
RESUME=$(slack ask "@bob" "本番に出してよい?" "出してよい" "待って" --code=<code>)
|
|
116
|
+
# -> slack ask --waitFor='https://acme.slack.com/archives/C00000001/p1700000000000100'
|
|
117
|
+
# Run it whenever you like: it re-reads the question from Slack, so nothing is
|
|
118
|
+
# stored locally and any machine holding the link can collect. Same stdout/exit
|
|
119
|
+
# contract as --wait, plus --timeout 0 = check once (exit 2 while still open),
|
|
120
|
+
# which is what a periodic monitor should use instead of parking on --wait.
|
|
121
|
+
eval "$RESUME --timeout 0" && echo answered
|
|
122
|
+
# A pressed pill is invisible to `slack tail` (it only sees `type: "message"`
|
|
123
|
+
# events and drops `message_changed`), so this is the only way to hear about an
|
|
124
|
+
# answer to a question you did not block on.
|
|
125
|
+
|
|
96
126
|
# Task tracking on top of reactions — :pushpin: marks a message as a task,
|
|
97
127
|
# a second reaction carries its progress (see "todo" below)
|
|
98
128
|
slack todo ls # my open tasks
|
|
@@ -238,7 +268,12 @@ Requires a Slack user token (`xoxp-...`) with the following scopes:
|
|
|
238
268
|
- `channels:read`, `groups:read`, `im:read`, `mpim:read` — for channel listing
|
|
239
269
|
- `users:read` — for resolving display names
|
|
240
270
|
- `chat:write` — for sending messages
|
|
241
|
-
- `reactions:write` — for `react` (
|
|
271
|
+
- `reactions:write` — for `react` and for `ask` (seeding / clearing the choice pills)
|
|
272
|
+
|
|
273
|
+
`ask` needs no `reactions:read`: it reads the answers back from
|
|
274
|
+
`conversations.history`, which returns each message's `reactions` (with the user
|
|
275
|
+
list) under the `*:history` scopes above. `reactions.get` would need
|
|
276
|
+
`reactions:read`, so it is deliberately not used.
|
|
242
277
|
|
|
243
278
|
> Add each scope to the **token type the CLI actually uses**. The CLI defaults to the
|
|
244
279
|
> **user token** (`xoxp-...`), so `reactions:write` must be under **User Token Scopes**.
|
package/SKILL.md
CHANGED
|
@@ -85,6 +85,14 @@ slack delete "<permalink>"
|
|
|
85
85
|
slack react "#general:1700000000.000100" white_check_mark
|
|
86
86
|
slack react "<permalink>" eyes --remove # take it back
|
|
87
87
|
|
|
88
|
+
# Ask a question with its choices pre-seeded as 1️⃣..🔟 reactions (same confirm gate)
|
|
89
|
+
# The question MUST @tag whoever may answer — only their reaction/reply counts.
|
|
90
|
+
slack ask "#eng" "@alice この PR 出してよい?" "出す" "待つ"
|
|
91
|
+
ANS=$(slack ask "@bob" "出してよい?" "はい" "待って" --code=<code> --wait) # blocks
|
|
92
|
+
RESUME=$(slack ask "@bob" "出してよい?" "はい" "待って" --code=<code>) # does not
|
|
93
|
+
# RESUME -> slack ask --waitFor='<permalink>' # collect the answer later
|
|
94
|
+
eval "$RESUME --timeout 0" # check once: 0 answered / 2 still open
|
|
95
|
+
|
|
88
96
|
# Task tracking on reactions — 📌 marks a task, a second reaction holds progress
|
|
89
97
|
slack todo ls # EVERYONE's open tasks (has:)
|
|
90
98
|
slack mytodo ls # only tasks YOU reacted to (hasmy:)
|
|
@@ -138,6 +146,37 @@ and no one has replied since — `⚠ 相手はまだ返信していません…
|
|
|
138
146
|
on that message instead of piling on a new one. Reply-status based, not time-based; opt
|
|
139
147
|
out with `SLACK_UNREPLIED_WARN=0`.
|
|
140
148
|
|
|
149
|
+
### ask — a question whose answer is one tap
|
|
150
|
+
|
|
151
|
+
Choices are posted as 1️⃣..🔟 reactions on the question itself, so answering costs one tap
|
|
152
|
+
on an existing pill. Reactions, not Block Kit buttons: `block_actions` are delivered only
|
|
153
|
+
to a Request URL or a Socket Mode socket, neither of which a short-lived CLI can own
|
|
154
|
+
without losing clicks. A reaction is durable state anyone can read back later.
|
|
155
|
+
|
|
156
|
+
- **The question must @tag whoever may answer** (`@alice`, or `@here`/`@channel` for the
|
|
157
|
+
whole channel). Only their reaction/reply is taken as the answer; an unaddressed
|
|
158
|
+
question is refused (exit 3) rather than let the first bystander decide it. In a 1:1 DM
|
|
159
|
+
the other party counts automatically.
|
|
160
|
+
- **Answer paths**: a pill, or free text. In a DM a plain reply counts; in a channel only
|
|
161
|
+
reactions and thread replies do (a channel carries unrelated traffic).
|
|
162
|
+
- **Two pills pressed = no answer.** Changing your mind leaves both reactions in place, so
|
|
163
|
+
`ask` says so in the thread once and keeps waiting rather than guessing.
|
|
164
|
+
- **Exit codes** are the contract: `0` answered (the answer alone on stdout), `2` timed
|
|
165
|
+
out, `3` transport/config failure. Everything human-facing goes to stderr, so
|
|
166
|
+
`ANS=$(slack ask … --wait)` is safe.
|
|
167
|
+
|
|
168
|
+
**Collecting an answer you did not block on.** Without `--wait`, stdout is a runnable
|
|
169
|
+
`slack ask --waitFor='<permalink>'`. Run it any time — it re-reads the question from Slack
|
|
170
|
+
and recovers everything it needs by parsing the message, so nothing is stored locally and
|
|
171
|
+
any machine with the link can collect. It reports an already-answered question (the
|
|
172
|
+
✅-stamped body) immediately, and refuses (exit 3) a message that is not an ask.
|
|
173
|
+
`--timeout 0` checks exactly once, which is what a periodic monitor should use: `--wait`
|
|
174
|
+
parks the process, and a parked lane still looks *active* in `ay ls`.
|
|
175
|
+
|
|
176
|
+
This path is not optional convenience — **a pressed pill is invisible to `slack tail`**,
|
|
177
|
+
which only sees `type: "message"` events and drops `message_changed`. Without `--waitFor`
|
|
178
|
+
an answer can be pressed and nobody ever hears about it.
|
|
179
|
+
|
|
141
180
|
### todo — tasks as reactions
|
|
142
181
|
|
|
143
182
|
A task is any message carrying the marker reaction 📌 `:pushpin:`. Progress lives in a
|