@avanderhoorn/toy-box-slack 0.1.0 → 0.2.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 CHANGED
@@ -1,78 +1,160 @@
1
- # toy-box-slack
1
+ # 💬 Toy Box Slack
2
2
 
3
- One local bridge lets a [Toy Box](https://github.com/lostintangent/toy-box) channel's Copilot lead answer allowed people in one Slack channel, publish scheduled release notes there, and bring useful activity back to Toy Box.
3
+ Toy Box Slack lets a [Toy Box](https://github.com/lostintangent/toy-box) channel's Copilot lead work in one Slack channel. People you allow ask the lead questions there and get answers in their threads, the lead can publish release notes on a schedule, and useful Slack activity comes back to the Toy Box channel for discussion.
4
4
 
5
- The four v1 guarantees are: only allowlisted people can open requests, only the owner widens access, uncertain posts are never resent, and events survive acknowledgement with gaps recovered or reported. Rare channel or policy changes suspend the link for owner review. The bridge keeps only what its links need, by default what allowed people say and the threads they ask in, and forgets any Slack text 30 days after it last changed. See [operations](docs/operations.md) for recovery and retention limits.
5
+ It runs as one local bridge beside Toy Box on your Mac. Only the bridge holds the Slack tokens, and it connects through Socket Mode, so it needs no public URL. The lead reaches Slack only through the `toy-box-slack` CLI.
6
6
 
7
- No DMs, attachments, Slack Connect, org-wide installs, rotating tokens or non-Copilot leads. Slack text the lead reads reaches Copilot and its selected model provider. Obtain company approval first and read the [reviewer guide](docs/security-review.md).
7
+ ## Features
8
8
 
9
- ## Before you start
9
+ - **Answers in threads:** When someone you allow mentions the app, the bridge wakes the channel's lead, which reacts with 👀 as it reads the request and answers once in its thread.
10
+ - **Only the people you allow:** Only people on the link's allowlist, by exact Slack member ID, can make the lead act, and the bridge checks that before the lead sees anything. Only you widen access, at your terminal, while the lead can only narrow it.
11
+ - **Nothing lost or sent twice:** Every Slack event the bridge acknowledges survives a crash, and anything it missed while stopped is recovered or reported. A post whose delivery is uncertain is looked up in Slack, never sent again.
12
+ - **Release notes and digests:** Optional routines publish release notes in the Slack channel, and bring a digest of what people said there back to the Toy Box channel.
13
+ - **Several leads:** One bridge hosts several Slack apps, so leads that share a Slack channel each answer as their own bot.
14
+ - **Keeps little:** By default the bridge keeps only what allowed people say and the threads they ask in, and it forgets any Slack text 30 days after it last changed.
15
+ - **Stops when the channel changes:** If the channel is shared outside your organization, made public or archived, or the app is removed from it, the bridge suspends the link for you to review.
10
16
 
11
- On an Apple silicon Mac, install [Node 24](https://nodejs.org/en/download). With the Mac's default zsh, put `~/.local/bin` on your PATH, then install Copilot, Toy Box and toy-box-slack:
17
+ It doesn't support DMs, attachments, Slack Connect, org-wide installs, rotating tokens, or Codex and Claude Code leads.
12
18
 
13
- ```sh
14
- export PATH="$HOME/.local/bin:$PATH"
15
- printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.zshrc"
16
- npm install --global --prefix "$HOME/.local" @github/copilot @lostintangent/toy-box @avanderhoorn/toy-box-slack
17
- ```
19
+ > ⚠️ Slack text the lead reads reaches Copilot and its selected model provider. Get your company's approval before you link a workspace, and read the [reviewer guide](docs/security-review.md).
18
20
 
19
- Run `copilot`, use `/login` to sign in, then exit. Start Toy Box **from this PATH**, so its leads can find `toy-box-slack`:
21
+ ## Getting Started
20
22
 
21
- ```sh
22
- toy-box --host 127.0.0.1
23
- ```
23
+ You need an Apple silicon Mac with [Node 24](https://nodejs.org/en/download), and a Slack workspace you're approved to add an app to.
24
+
25
+ 1. Put `~/.local/bin` on your PATH, and install Copilot, Toy Box and Toy Box Slack there:
26
+
27
+ ```sh
28
+ export PATH="$HOME/.local/bin:$PATH"
29
+ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
30
+ npm install --global --prefix "$HOME/.local" \
31
+ @github/copilot @lostintangent/toy-box @avanderhoorn/toy-box-slack
32
+ ```
33
+
34
+ 1. Run `copilot`, sign in with `/login`, then exit.
24
35
 
25
- Toy Box has no authentication. Never expose it publicly or through a tunnel.
36
+ 1. Start Toy Box from that PATH, so its leads can find `toy-box-slack`, and keep it running:
26
37
 
27
- ## Set up
38
+ ```sh
39
+ toy-box --host 127.0.0.1
40
+ ```
41
+
42
+ > ⚠️ Toy Box has no authentication. Never expose it publicly or through a tunnel.
28
43
 
29
- Toy Box keeps its terminal, so use another for these three steps.
44
+ 1. Create the Slack app:
45
+ - At <https://api.slack.com/apps>, choose **Create New App → From a manifest**, pick the approved workspace, and paste [docs/manifest.yaml](docs/manifest.yaml).
46
+ - Review its eight bot scopes, create the app, and install it from **OAuth & Permissions**, which then shows its **Bot User OAuth Token** (`xoxb-...`).
47
+ - Under **Basic Information → App-Level Tokens**, generate a token (`xapp-...`) with the `connections:write` scope.
48
+ - Leave token rotation off. The manifest already turns on Socket Mode, so the app needs no Request URL.
30
49
 
31
- 1. **Create the Slack app.** At <https://api.slack.com/apps>, choose **Create New App > From a manifest**, choose the approved workspace, and paste [docs/manifest.yaml](docs/manifest.yaml). Review the eight bot scopes and create the app. Install it to that workspace from **OAuth & Permissions**, which then shows its **Bot User OAuth Token** (`xoxb-...`). Under **Basic Information > App-Level Tokens**, generate an app-level token (`xapp-...`) with `connections:write`. The manifest already enables Socket Mode, so no public Request URL is needed. Do not enable token rotation.
50
+ 1. In another terminal, run `toy-box-slack serve` and keep it running. The first time, it asks for both tokens, without showing what you type.
51
+ - Paste the tokens only there, never into Toy Box, Slack or the repository.
52
+ - Each time it starts, `serve` installs the skill leads learn the CLI from, and names its file.
32
53
 
33
- 2. **Start the bridge.** Run `toy-box-slack serve` and keep it running. The first time, it asks for both tokens and for a passphrase you choose, typed twice. Paste the tokens only there, never into Toy Box, Slack or the repository. Nothing you type is shown. `link`, `allow` and `resume` ask for the passphrase before they widen who can make the lead act, so make it strong and unique, and never put it in Toy Box or Slack. Each time it starts, `serve` installs the skill leads learn the CLI from, and names the file.
54
+ 1. In Slack, invite the app to the channel with `/invite` and select **Toy Box**. Copy the channel's ID from the bottom of its details, and each allowed person's member ID from their profile.
34
55
 
35
- 3. **Link the channels.** Invite the app into the Slack channel with `/invite` and select **Toy Box**. Open <http://127.0.0.1:3000> and create a channel with a Copilot lead (tested with `claude-sonnet-5`). To use a lead that was already running instead, ask it to read the file `serve` named, since Copilot reads skills when a lead's session starts or resumes. Copy the Slack channel ID from the bottom of its details and the allowed people's member IDs from their profiles, then ask the lead to set up Slack for those IDs. It creates its inbox routine and prints the exact `link` and `allow` commands. Run them yourself in another terminal, and review each preview before you enter the passphrase. Their forms are:
56
+ 1. Open <http://127.0.0.1:3000>, create a channel with a Copilot lead (tested with `claude-sonnet-5`), and ask it to set up Slack for those IDs. To use a lead that was already running, first ask it to read the skill file `serve` named, since Copilot reads skills when a session starts or resumes.
57
+
58
+ 1. The lead creates its requests routine and gives you a `link` and an `allow` command. Run them in another terminal. Each says what it changed, with names from Slack, and `deny` or `unlink` undoes it:
36
59
 
37
60
  ```sh
38
61
  toy-box-slack link --channel TOYBOX_ID --slack-channel SLACK_ID
39
62
  toy-box-slack allow --channel TOYBOX_ID USER_ID
40
63
  ```
41
64
 
42
- Defaults are `--requests mention --observe requesters`; `--requests all` accepts any allowed person's top-level post, and `--observe channel` lets a digest read other people's text too. Ask the lead for opt-in digest or release-note routines if wanted.
65
+ 1. Send a **new** mention, picking the app from Slack's mention menu. Within minutes, you should see 👀 on it and one answer in its thread. If not, run `toy-box-slack status` and see the [operations guide](docs/operations.md).
43
66
 
44
- Send a **new** mention by selecting the app in Slack's mention menu. An allowed person's question should get eyes and one threaded answer within minutes. If it does not, check `toy-box-slack status` and the [operations guide](docs/operations.md).
67
+ ## Link Options
45
68
 
46
- ## Several leads in one Slack channel
69
+ By default, an allowed person asks the lead by mentioning the app, and the lead reads only what allowed people say and the threads they ask in. To widen that, add either option to `link`, or run `link` again with it later:
47
70
 
48
- People tell leads apart by the bot they mention, so leads in the same Slack channel each need a Slack app of their own, while leads in different Slack channels share one. Create another Slack app, add it to the bridge with `toy-box-slack add-app <name>`, and link through it with `--app <name>`, as [operations](docs/operations.md#add-or-remove-an-app) describes. Ask people there to mention the lead they mean, since a reply that mentions no one reaches every lead its author asked in that thread. Two links with `--requests all` both answer each top-level post that mentions no one, from someone they both allow.
71
+ - `--requests all` makes any top-level post from an allowed person a request too.
72
+ - `--observe channel` lets the lead read the whole channel, so digests cover everyone.
49
73
 
50
- ## Keep it running
74
+ `--requests mention` and `--observe requesters` set them back.
51
75
 
52
- The bridge answers only while `serve` runs. Ctrl-C stops it. When `serve` runs again, it catches up on what it missed, within the [limits](docs/operations.md#deletion-and-limits) of catch-up. To start it at login instead, see [operations](docs/operations.md#start-the-bridge-at-login).
76
+ ## Routines
53
77
 
54
- `serve` keeps the tokens and the passphrase's hash in `~/.toy-box-slack/credentials.env`, which only you can read. The bridge reads this file, not a `.env`, and environment variables of the same names override it. `TOY_BOX_SLACK_HOME` selects another data folder, and `TOY_BOX_URL` a Toy Box other than <http://127.0.0.1:3000>. `toy-box-slack passphrase` changes the passphrase, given the old one, without a restart.
78
+ The lead does its Slack work in Toy Box routines it creates from the skill. Ask it for a different schedule if these don't suit you.
55
79
 
56
- To update, run `npm install --global --prefix "$HOME/.local" @avanderhoorn/toy-box-slack` again, then restart `serve`, or the [login job](docs/operations.md#start-the-bridge-at-login) that runs it. `serve` installs the new release's skill, so ask your leads to set their routines again from it, since a routine keeps the prompt it was created with.
80
+ - **Slack requests:** The lead answers each waiting request in its Slack thread, whenever the bridge wakes it and on weekdays at 9:00. It creates this routine as it sets up Slack.
81
+ - **Slack digest:** If you ask for it, the lead posts what's worth the team's attention in the Toy Box channel on weekdays at 17:00.
82
+ - **Release notes:** If you ask for it, the lead publishes notes in the Slack channel on what the git log and completed tasks say shipped, on Fridays at 16:00 or every weekday.
57
83
 
58
- ## Commands
84
+ ## Several Leads in One Slack Channel
59
85
 
60
- **Lead:** `inbox`, `reply`, `dismiss`, `activity`, `mark-read`, `publish`, `retract`. **Owner:** `passphrase`, `add-app`, `link`, `allow`, `resume`. **Lead or terminal:** `status`, `deny`, `pause`, `unlink`. **Operator:** `serve`.
86
+ People tell leads apart by the app they mention, so leads that share a Slack channel each need their own Slack app, while leads in different Slack channels can share one. To add an app for another lead:
61
87
 
62
- `toy-box-slack <command> --help` gives flags. See [operations](docs/operations.md) for status, pauses, uncertain posts, deletion and starting at login, [AGENTS.md](AGENTS.md) for architecture, and [research](docs/research) for evidence.
88
+ 1. Create another Slack app from the manifest, changing only its `name` and `display_name`. Install it, generate its app-level token, and invite it to the channel.
89
+ 1. In a terminal, run `toy-box-slack add-app <name>`, which asks for the app's two tokens.
90
+ 1. Restart `serve`, which starts every app it hosts.
91
+ 1. Ask the new lead to set up Slack. Its `link` command names the app with `--app <name>`.
63
92
 
64
- ## Developing
93
+ Ask people there to mention the lead they mean, since a reply that mentions no one reaches every lead its author asked in that thread. Two links with `--requests all` both answer a top-level post that mentions no one, from someone they both allow. The [operations guide](docs/operations.md#add-or-remove-an-app) covers app names, Slack's limits and removing an app.
65
94
 
66
- 1. Install [Bun](https://bun.com/docs/installation) 1.4.4, which CI builds with, and clone this repo
67
- 1. Run `bun install`, which also points Git at the pre-commit hook
68
- 1. Run `bun run check` and `bun test`, which CI runs on every push and pull request
95
+ ## Keeping It Running
96
+
97
+ The bridge answers only while `serve` runs, and Ctrl-C stops it. When `serve` runs again, it catches up on what it missed, within [limits](docs/operations.md#deletion-and-limits). To start it at login instead, see the [operations guide](docs/operations.md#start-the-bridge-at-login).
98
+
99
+ `serve` keeps the tokens in `~/.toy-box-slack/credentials.env`, which only you can read. The bridge reads this file, not a `.env`, and environment variables of the same names override it.
69
100
 
70
- `./cli/index.ts` runs the CLI from source. `bun run build` compiles the binary the npm package ships, and `./toy-box-slack` runs it as npm installs it.
101
+ | Variable | Default | What it sets |
102
+ | -------------------- | ----------------------- | ---------------------------------------- |
103
+ | `TOY_BOX_SLACK_HOME` | `~/.toy-box-slack` | The bridge's data folder |
104
+ | `TOY_BOX_URL` | `http://127.0.0.1:3000` | The Toy Box whose leads the bridge wakes |
71
105
 
72
- ## Publishing
106
+ ## Updating
73
107
 
74
- Update `package.json` to the next stable version and keep its release notes under a matching `## 📅 vX.Y.Z (date)` heading in `CHANGELOG.md`. Commit and push those changes, then select **Actions → Publish → Run workflow** on the branch to release.
108
+ Install the new release, then restart `serve`, or the [login job](docs/operations.md#start-the-bridge-at-login) that runs it:
75
109
 
76
- The workflow reads the version and notes from that exact commit, rejects an already published npm version, runs checks and tests, and builds the Apple silicon macOS CLI. It publishes the tested package through npm trusted publishing, then creates the matching GitHub tag and release with the changelog notes and npm tarball attached. No npm token or separately entered release version is needed.
110
+ ```sh
111
+ npm install --global --prefix "$HOME/.local" @avanderhoorn/toy-box-slack
112
+ ```
113
+
114
+ `serve` installs the new release's skill. A routine keeps the prompt it was created with, so ask each lead to set its routines again from the new skill.
115
+
116
+ ## Uninstalling
117
+
118
+ 1. Ask each lead to delete its Slack routines.
119
+ 1. Stop `serve`, or stop the [login job](docs/operations.md#start-the-bridge-at-login) and delete its plist.
120
+ 1. Uninstall the CLI, and delete its data folder and skill:
121
+
122
+ ```sh
123
+ npm uninstall --global --prefix "$HOME/.local" @avanderhoorn/toy-box-slack
124
+ rm -rf ~/.toy-box-slack ~/.copilot/skills/toy-box-slack
125
+ ```
126
+
127
+ 1. Uninstall each Slack app at <https://api.slack.com/apps>.
128
+
129
+ Copies outside the bridge stay, such as Toy Box transcripts and posts already in Slack, as the [reviewer guide](docs/security-review.md#retention-and-deletion) explains.
130
+
131
+ ## Commands
132
+
133
+ | Command | Who runs it | What it does |
134
+ | ----------- | --------------- | ------------------------------------------------------------- |
135
+ | `serve` | You | Run the bridge and install its skill |
136
+ | `add-app` | You | Add another Slack app for the bridge to host |
137
+ | `link` | You | Link a Toy Box channel to a Slack channel, or change the link |
138
+ | `allow` | You | Let people make the lead act, by Slack member ID |
139
+ | `resume` | You | Answer and post again after a pause or suspension |
140
+ | `status` | You or the lead | Show the link, Slack's connection, requests and wakes |
141
+ | `deny` | You or the lead | Disallow people, and cancel their requests |
142
+ | `pause` | You or the lead | Stop answering and posting in Slack until you resume |
143
+ | `unlink` | You or the lead | Remove the link and delete what the bridge kept for it |
144
+ | `inbox` | The lead | Read waiting requests, with their threads so far |
145
+ | `reply` | The lead | Answer a request in its Slack thread, once |
146
+ | `dismiss` | The lead | End a request that needs no answer |
147
+ | `activity` | The lead | Read what's new in the Slack channel |
148
+ | `mark-read` | The lead | Mark activity read, so the next read starts after it |
149
+ | `publish` | The lead | Post once in Slack for a routine's latest scheduled run |
150
+ | `retract` | The lead | Delete one of its confirmed posts from Slack |
151
+
152
+ Run `toy-box-slack <command> --help` for its options. The [operations guide](docs/operations.md) covers status, pauses, uncertain posts, deletion and starting at login, and [research](docs/research) holds the evidence behind the design.
153
+
154
+ ## Developing
155
+
156
+ 1. Install [Bun](https://bun.com/docs/installation) 1.4.4, which CI uses, and clone this repo
157
+ 1. Run `bun install`, which also points Git at the pre-commit hook
158
+ 1. Run `bun run check` and `bun test`
77
159
 
78
- If a run fails before npm publication, fix the failure and rerun. If npm succeeds but GitHub release creation fails, recover the tarball from npm with `npm pack @avanderhoorn/toy-box-slack@X.Y.Z --ignore-scripts`, verify its registry integrity, and use that tarball and the original commit's changelog notes to finish the GitHub release manually. Do not rebuild or republish the same npm version.
160
+ `./cli/index.ts` runs the CLI from source. `bun run build` compiles the binary the npm package ships, and `./toy-box-slack` runs it as npm installs it. [AGENTS.md](AGENTS.md) covers the architecture, testing and releasing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avanderhoorn/toy-box-slack",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Let a Toy Box channel's lead watch and answer a Slack channel",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/toy-box-slack-darwin-arm64
3
3
  size: 89605730 bytes
4
- sha256: e0415f8e14a1ca4499e44b2e404e1fa09e4d51cd63342cdc47db6621936b27f1
4
+ sha256: 57c92145edf4de9a019bae0aba576082b8389b98efa1c6e11d489f15c9f3275b