@interactive-inc/claude-funnel 0.28.0 → 0.29.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
@@ -102,11 +102,7 @@ Or drop a `funnel.json` in the repo and `fnl claude` (no args) inside the repo w
102
102
  "connectors": [
103
103
  {
104
104
  "type": "slack",
105
- "name": "my-slack",
106
- "env": {
107
- "botToken": "SLACK_BOT_TOKEN",
108
- "appToken": "SLACK_APP_TOKEN"
109
- }
105
+ "name": "my-slack"
110
106
  }
111
107
  ]
112
108
  },
@@ -132,21 +128,18 @@ Or drop a `funnel.json` in the repo and `fnl claude` (no args) inside the repo w
132
128
 
133
129
  A channel declares only transport — its `connectors` and delivery mode. The launch recipe lives on `profiles[]`: each profile binds to a channel by name and carries `options` (prepended to the claude argv before user-supplied CLI args, which still come last — use it for flags like `--brief`, `--agent <name>`, `--model <name>`), `env` (layered under the launched claude process — `process.env` from the launching shell wins on collision), and `resume`. `fnl claude` applies the first profile bound to the chosen channel.
134
130
 
135
- The optional `connectors` array on a channel is the source of truth for that channel: missing connectors are created, an existing connector matched by token under a different name is renamed in place, and connectors not declared are removed on launch. An absent `connectors` field leaves `~/.funnel` alone.
131
+ The optional `connectors` array on a channel is the source of truth for that channel: missing connectors are created and connectors not declared are removed on launch. Connectors are matched by name. An absent `connectors` field leaves existing connectors alone.
136
132
 
137
- On launch, the chosen channel's transport (`connectors`, delivery) is materialized into the global `~/.funnel/settings.json` Channel entry; the profile recipe is passed straight to the launcher and is not persisted there. Raw launches (`fnl claude --channel <name>` without funnel.json) bind transport only and carry no recipe.
133
+ A repo with a `funnel.json` is scoped to itself. On first launch funnel writes a stable `id` (uuid) to the top of `funnel.json` and keeps every byte of funnel state under `~/.funnel/projects/<id>/`, never touching the global `~/.funnel` — only the event log and temp files under `/tmp/funnel/` are shared. This scoping applies to every CLI command run in the repo, not just `fnl claude`. On launch the chosen channel's transport (`connectors`, delivery) is materialized into `~/.funnel/projects/<id>/settings.json`; the profile recipe is passed straight to the launcher and is not persisted there. Raw launches (`fnl claude --channel <name>` without funnel.json) bind transport only against the global `~/.funnel` and carry no recipe.
138
134
 
139
135
  The optional top-level `$schema` points at the JSON Schema so editors can validate and autocomplete the file. The recommended reference for repos with a local install is `./node_modules/@interactive-inc/claude-funnel/funnel.schema.json` — it works without a network round-trip and editors do not need to prompt for trust. The same file is also published at `https://interactive-inc.github.io/open-claude-funnel/funnel.schema.json` (editors usually require explicit trust on first use), and `fnl schema > funnel.schema.json` regenerates a local copy on demand.
140
136
 
141
- Each token field resolves in this order:
137
+ Connectors in `funnel.json` carry no tokens. A connector's token is set out of band and stored only in the repo-scoped settings:
142
138
 
143
- - literal value at the field itself (e.g. `"botToken": "xoxb-..."`) used as-is
144
- - env-var name at `env.<field>` (e.g. `"env": { "botToken": "SLACK_BOT_TOKEN" }`) looked up in `process.env`, falling back to `./.env.local` in the cwd; fails with a clear error when neither is set
145
- - field omitted everywhere — `fnl claude` prompts on a TTY and writes the answer to `~/.funnel/settings.json`; on non-TTY stdin the launch fails so CI / agent-spawned-agent runs do not hang
139
+ - run `fnl channels <ch> connectors set <c> --bot-token=...` (etc.) in the repo to write it to `~/.funnel/projects/<id>/settings.json`
140
+ - or omit it `fnl claude` prompts on a TTY at launch and saves the answer there; it carries over so later launches do not re-prompt. On non-TTY stdin the launch fails so CI / agent-spawned-agent runs do not hang
146
141
 
147
- Setting both a literal and an `env.<field>` for the same field is an error (pick one).
148
-
149
- `funnel.json` itself is never written to — secrets stay in env vars, `.env.local`, or `~/.funnel`, never in the committed file.
142
+ Only the `id` is ever written back to `funnel.json`; tokens never live in the repo.
150
143
 
151
144
  Cron-driven agent runs:
152
145
 
@@ -271,21 +264,23 @@ Profile = { id, name, path, channelId, options[], env, resume, sessionId? }
271
264
  name is the CLI handle. sessionId is execution state, not config — the claude session this
272
265
  profile last launched, written by the launcher and read back on the next resume.
273
266
 
274
- LocalConfig = { channels: ChannelSpec[], profiles?: ProfileSpec[] }
267
+ LocalConfig = { id?, channels: ChannelSpec[], profiles?: ProfileSpec[] }
275
268
  per-repo file (funnel.json). channels[] required; first entry is default, --channel selects.
269
+ id (uuid) is written back on first launch; this repo's funnel state lives under
270
+ ~/.funnel/projects/<id>/ and the global ~/.funnel is never touched.
276
271
 
277
272
  ChannelSpec = { name, connectors? }
278
273
  transport declaration (no id, since funnel.json declares by name). connectors materialize into
279
- the matching Channel in ~/.funnel/settings.json on launch. Connector token fields accept a
280
- literal, an env-var reference at `env.<field>` resolved from process.env and ./.env.local, or
281
- omission for a TTY prompt persisted to ~/.funnel.
274
+ the matching Channel in ~/.funnel/projects/<id>/settings.json on launch. Connectors carry no
275
+ tokens; a token is set via the CLI or prompted on a TTY at launch and saved to that scoped settings.
282
276
 
283
277
  ProfileSpec = { channel, options?, env?, resume? }
284
278
  launch recipe bound to a channel by name. applied inline on launch (the first spec bound to the
285
279
  chosen channel — selected by its channel binding, not by name); not persisted into the global
286
280
  profiles[] list.
287
281
 
288
- Settings = { channels[], profiles[] } → ~/.funnel/settings.json
282
+ Settings = { channels[], profiles[] } → ~/.funnel/settings.json (global)
283
+ or ~/.funnel/projects/<id>/settings.json (per-repo funnel.json)
289
284
  ```
290
285
 
291
286
  ## File layout
@@ -294,7 +289,10 @@ Persistent state lives under `~/.funnel/`. Volatile logs and the event log live
294
289
 
295
290
  ```
296
291
  ~/.funnel/
297
- ├── settings.json channels[] with nested connectors, profiles[]
292
+ ├── settings.json global channels[] with nested connectors, profiles[]
293
+ ├── projects/
294
+ │ └── <id>/ per-repo state for a repo with funnel.json
295
+ │ └── settings.json, gateway.token, claude/, ... (same layout as the global root, scoped by funnel.json id)
298
296
  ├── gateway.pid daemon PID
299
297
  ├── gateway.token Bearer token for daemon HTTP / WS
300
298
  ├── claude/