slack-term 1.40.0 → 1.41.1
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 +19 -3
- package/SKILL.md +50 -1
- package/dist/cli.js +227 -190
- package/package.json +1 -1
- package/ts/ask.ts +377 -45
- package/ts/botIntent.ts +68 -0
- package/ts/cli.ts +1026 -40
- package/ts/slack.ts +12 -0
package/README.md
CHANGED
|
@@ -123,11 +123,15 @@ slack react "<permalink>" eyes --remove # take a reaction back
|
|
|
123
123
|
|
|
124
124
|
# Ask a question with its choices pre-seeded as 1️⃣..🔟 reactions — answering is
|
|
125
125
|
# one tap on an existing pill, no emoji picker. Same two-step confirm gate as send.
|
|
126
|
+
# A ❓ "その他 (other)" pill always follows the choices: pressing it means "none of
|
|
127
|
+
# these" and the answer comes as a reply (exit 5), not as the ❓ itself.
|
|
128
|
+
# The instructions are posted in ja or en: --lang / SLACK_TERM_LANG, else the
|
|
129
|
+
# answerers' Slack locale, else the question's own language, else $LANG, else ja.
|
|
126
130
|
slack ask "@bob" "本番に出してよい?" "出してよい" "待って"
|
|
127
131
|
# --wait blocks until answered and prints ONLY the answer on stdout, so it composes:
|
|
128
132
|
ANS=$(slack ask "@bob" "本番に出してよい?" "出してよい" "待って" --code=<code> --wait)
|
|
129
133
|
# exit 0 = answered, 2 = timed out (--timeout, default 3600s), 3 = transport failure,
|
|
130
|
-
# 4 = a reply matched several choices, 5 = a free-text reply that picked NO choice —
|
|
134
|
+
# 4 = a reply matched several choices, 6 = the question was voided, 5 = a free-text reply that picked NO choice —
|
|
131
135
|
# the reply text is on stdout, it is not a decision, and the question stays open.
|
|
132
136
|
#
|
|
133
137
|
# The question must say WHO may answer — only their reaction/reply is taken as the
|
|
@@ -138,8 +142,20 @@ slack ask "#eng" "@here 誰か見れる?" "見る" "あとで" # anyo
|
|
|
138
142
|
#
|
|
139
143
|
# With no choices the question asks for a free-text reply and the reply is the answer.
|
|
140
144
|
# In a DM a plain reply counts; in a channel only reactions and thread replies do.
|
|
141
|
-
# Once answered, the question is edited to "✅ …回答済み > <answer>"
|
|
142
|
-
#
|
|
145
|
+
# Once answered, the question is edited to "✅ …回答済み > <answer>" — the body/background
|
|
146
|
+
# and the CHOSEN option line stay; the other options, ❓ line and instructions go. The
|
|
147
|
+
# unpressed seeds are removed, leaving the chosen pill visible.
|
|
148
|
+
#
|
|
149
|
+
# Thread notes (audience replies that are not the answer, even after ✅) go to
|
|
150
|
+
# stderr; --json puts {answer, notes[], cursor} on stdout. Feed cursor back as --after:
|
|
151
|
+
# slack ask --waitFor='<permalink>' --timeout 0 --json --after=<cursor>
|
|
152
|
+
#
|
|
153
|
+
# Retire a question that expired / stopped meaning anything (無効化) — waits on it exit 6:
|
|
154
|
+
# slack ask --void='<permalink>' --reason 'head moved' --superseded-by '<new permalink>'
|
|
155
|
+
# Change one: slack ask --edit='<permalink>' ["new question" [choices…]] [--body …]
|
|
156
|
+
# (options only while unanswered; after that: --void='<permalink>' --reask …)
|
|
157
|
+
# List mine with live state: slack ask --ls [--stale 24h] [--state all] [--json]
|
|
158
|
+
# (`slack edit` refuses an edit that would break an ask/poll; --force overrides.)
|
|
143
159
|
#
|
|
144
160
|
# WITHOUT --wait, stdout is the command that collects the answer later:
|
|
145
161
|
RESUME=$(slack ask "@bob" "本番に出してよい?" "出してよい" "待って" --code=<code>)
|
package/SKILL.md
CHANGED
|
@@ -162,11 +162,19 @@ without losing clicks. A reaction is durable state anyone can read back later.
|
|
|
162
162
|
the other party counts automatically.
|
|
163
163
|
- **Answer paths**: a pill, or free text. In a DM a plain reply counts; in a channel only
|
|
164
164
|
reactions and thread replies do (a channel carries unrelated traffic).
|
|
165
|
+
- **❓ is the standing "other" choice.** Every question with choices lists it last
|
|
166
|
+
(`1️⃣ … 2️⃣ … 3️⃣ … ❓ その他 — スレッドで返信`) and seeds it after the keycaps. Pressing it
|
|
167
|
+
is not an answer — `--wait` notes it on stderr and keeps waiting for the reply, which
|
|
168
|
+
arrives as free text (exit `5`).
|
|
169
|
+
- **The instructions are posted in `ja` or `en`.** `--lang` (or `SLACK_TERM_LANG`) decides;
|
|
170
|
+
otherwise the answerers' Slack locale when they all share one, else the language the
|
|
171
|
+
question is written in, else the system locale, else `ja`. The confirm gate prints the
|
|
172
|
+
choice and why (`Language: en (answerers' Slack locale; …)`). `--waitFor` reads either.
|
|
165
173
|
- **Two pills pressed = no answer.** Changing your mind leaves both reactions in place, so
|
|
166
174
|
`ask` says so in the thread once and keeps waiting rather than guessing.
|
|
167
175
|
- **Exit codes** are the contract: `0` answered (the answer alone on stdout), `2` nobody
|
|
168
176
|
replied, `3` transport/config failure, `4` a reply matched several choices (stdout
|
|
169
|
-
empty), `5` a **free-text reply** that picked none of the offered choices — the reply
|
|
177
|
+
empty), `6` the question was **voided** (stop waiting), `5` a **free-text reply** that picked none of the offered choices — the reply
|
|
170
178
|
text is on stdout. Everything human-facing goes to stderr, so
|
|
171
179
|
`ANS=$(slack ask … --wait)` is safe.
|
|
172
180
|
- **A reply that chooses nothing is delivered, but is not a decision.** When choices were
|
|
@@ -177,6 +185,46 @@ without losing clicks. A reaction is durable state anyone can read back later.
|
|
|
177
185
|
so a pill pressed later still decides it: re-wait past the delivered reply with
|
|
178
186
|
`slack ask --waitFor='<permalink>' --after=<reply ts>` (stderr prints this line).
|
|
179
187
|
|
|
188
|
+
**Thread notes ride along with the answer.** A reply from the audience that is not the
|
|
189
|
+
answer (「あと cswap ls も見て」 next to a pressed pill) is a *note*. Notes are read even when
|
|
190
|
+
a pill answered, and again on every `--waitFor` of a ✅ question, so a note written after
|
|
191
|
+
the answer still reaches you. Plain mode: stdout is still the answer alone, and the notes go
|
|
192
|
+
to stderr. `--json`: stdout is one object
|
|
193
|
+
`{status, answer, how, who, notes:[{ts,user,text,permalink}], cursor, permalink}`, with the same
|
|
194
|
+
exit codes. Pass `cursor` back as `--after` on your next `--waitFor --timeout 0` and you
|
|
195
|
+
never get the same note twice. Only the question's audience counts; the asker, bots and
|
|
196
|
+
bystanders are never notes.
|
|
197
|
+
|
|
198
|
+
**Answering keeps the context.** The ✅ rewrite keeps the question's body (its background)
|
|
199
|
+
and the chosen option; only the options not chosen, the ❓ line and the instructions go.
|
|
200
|
+
The head is unchanged (`:white_check_mark:` prefix, question, stamp, quoted answer), so
|
|
201
|
+
anything that keys on the prefix or re-reads the answer works on old and new ✅ bodies alike.
|
|
202
|
+
|
|
203
|
+
**Voiding a question (無効化).** When a question has expired or stopped meaning anything
|
|
204
|
+
(the head it was pinned to moved, the decision was made elsewhere), retire it:
|
|
205
|
+
`slack ask --void='<permalink>' --reason '…' [--superseded-by '<new permalink>']`
|
|
206
|
+
(repeatable `--void=`; alias `slack ask void <link…>`; same `--code` gate; own questions
|
|
207
|
+
only, `--as-bot` for the bot's). The text becomes `:no_entry_sign:` + reason with the body
|
|
208
|
+
kept, our pills come off, and a 🚫 reaction is added. Three states, three prefixes:
|
|
209
|
+
`:question:` open, `:white_check_mark:` answered, `:no_entry_sign:` void. `--wait` /
|
|
210
|
+
`--waitFor` on a void question **exit 6** (`--json` → `status: "void"`): stop waiting.
|
|
211
|
+
Answered questions cannot be voided.
|
|
212
|
+
|
|
213
|
+
**Changing a question: `--edit`, not `slack edit`.**
|
|
214
|
+
`slack ask --edit='<permalink>' ["new question" [choices…]] [--body …]` rebuilds it with the
|
|
215
|
+
ask layout (same `--code` gate), so collect still reads it. Wording can change any time;
|
|
216
|
+
the **options** only while nobody has answered. After that, re-ask it:
|
|
217
|
+
`slack ask --void='<permalink>' --reask [--reason …] ["new question" [choices…]]` posts the
|
|
218
|
+
question again in the same place (old text/choices/body unless you give new ones) and voids
|
|
219
|
+
the old one as replaced by it — one gate for both. Plain `slack edit` refuses an edit that
|
|
220
|
+
would break an ask/poll (text no longer readable, or an answered/void question made open
|
|
221
|
+
again); edits that keep it readable — e.g. a script writing the ✅ form — pass as usual.
|
|
222
|
+
|
|
223
|
+
**What did I ask, and what became of it.** `slack ask --ls` lists your asks from the local
|
|
224
|
+
sent-log with their LIVE state (`--state open|answered|void|all`, default open; `--channel`;
|
|
225
|
+
`--json`). `--ls --stale 24h` = open and older than 24h, and prints the one `--void=` command
|
|
226
|
+
that would retire them all (behind the usual gate). Only asks sent from this machine.
|
|
227
|
+
|
|
180
228
|
**Collecting an answer you did not block on.** Without `--wait`, stdout is a runnable
|
|
181
229
|
`slack ask --waitFor='<permalink>'`. Run it any time — it re-reads the question from Slack
|
|
182
230
|
and recovers everything it needs by parsing the message, so nothing is stored locally and
|
|
@@ -367,6 +415,7 @@ slack news --limit 1
|
|
|
367
415
|
- **`conversations.invite` reports success but the member never shows up** — the invited user is likely a single-channel guest (`is_ultra_restricted`); Slack silently no-ops the invite since that account type can only ever belong to the one channel it was created in. `channel create --invite` now warns about this up front.
|
|
368
416
|
- **Send is rejected with "use #channel or @user"** — the CLI enforces human-readable targets. Use `#channel-name` or `@display-name`, not raw IDs.
|
|
369
417
|
- **Confirm code mismatch on any gated write** — the content, target or acting identity changed between preview and confirm (a different profile/`SLACK_TOKEN`, or `--as-bot` added or dropped). Re-run without `--code` for a fresh preview, and re-check the `From:` line and the `→` destination line (THREAD REPLY vs NEW top-level message).
|
|
418
|
+
- **`refusing to act as YOU — SLACK_BOT_TOKEN is exported …`** — you set a bot token in the shell or a project `.env` (not the global `~/.config/slack-cli/.env`) but ran a write without `--as-bot`, which would post as the logged-in person. Add `--as-bot` to post as the app; to post as yourself, set `SLACK_TOKEN` or clear the bot token for that run (`SLACK_BOT_TOKEN= slack …`). Reads are never refused.
|
|
370
419
|
- **Enterprise Grid / admin-locked workspace** — custom app installation may need admin approval or be disabled outright.
|
|
371
420
|
|
|
372
421
|
## Safety
|