@interactive-inc/claude-funnel 0.4.1 → 0.8.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 +233 -111
- package/dist/bin.js +1417 -0
- package/dist/gateway/daemon.js +513 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/lib/bin.ts +78 -0
- package/lib/{modules → cli}/router/to-request.ts +13 -20
- package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +27 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +40 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +41 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +22 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +23 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.ts +26 -0
- package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +92 -0
- package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +22 -0
- package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +63 -0
- package/lib/cli/routes/channels.$channel.connectors.ts +26 -0
- package/lib/cli/routes/channels.$channel.rename.$newName.ts +22 -0
- package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +34 -0
- package/lib/cli/routes/channels.$channel.ts +34 -0
- package/lib/cli/routes/channels.add.$channel.ts +33 -0
- package/lib/cli/routes/channels.remove.$channel.ts +20 -0
- package/lib/cli/routes/channels.ts +39 -0
- package/lib/cli/routes/claude.ts +69 -0
- package/lib/cli/routes/gateway.listeners.ts +41 -0
- package/lib/cli/routes/gateway.logs.ts +123 -0
- package/lib/{routes/gateway/restart.ts → cli/routes/gateway.restart.ts} +20 -5
- package/lib/cli/routes/gateway.run.ts +41 -0
- package/lib/cli/routes/gateway.start.ts +50 -0
- package/lib/cli/routes/gateway.status.ts +19 -0
- package/lib/cli/routes/gateway.stop.ts +32 -0
- package/lib/cli/routes/gateway.ts +55 -0
- package/lib/cli/routes/index.ts +202 -0
- package/lib/cli/routes/profiles.$profile.as-default.ts +22 -0
- package/lib/cli/routes/profiles.$profile.rename.$newName.ts +22 -0
- package/lib/cli/routes/profiles.$profile.run.ts +36 -0
- package/lib/cli/routes/profiles.add.$profile.ts +46 -0
- package/lib/cli/routes/profiles.remove.$profile.ts +20 -0
- package/lib/cli/routes/profiles.set.$profile.ts +46 -0
- package/lib/cli/routes/profiles.ts +40 -0
- package/lib/cli/routes/status.ts +93 -0
- package/lib/cli/routes/update.ts +27 -0
- package/lib/connectors/connector-config-schema.ts +16 -0
- package/lib/connectors/connector-factory.ts +94 -0
- package/lib/connectors/connector-listener.ts +20 -0
- package/lib/{modules/connectors/funnel-discord-adapter.ts → connectors/discord-adapter.ts} +6 -11
- package/lib/{modules/connectors → connectors}/discord-connector-schema.ts +4 -1
- package/lib/connectors/discord-listener.ts +111 -0
- package/lib/{modules/connectors/funnel-gh-adapter.ts → connectors/gh-adapter.ts} +3 -6
- package/lib/{modules/connectors → connectors}/gh-connector-schema.ts +4 -1
- package/lib/{modules/connectors/funnel-gh-listener.ts → connectors/gh-listener.ts} +57 -22
- package/lib/{modules/connectors → connectors}/match-cron.ts +10 -4
- package/lib/connectors/schedule-connector-schema.ts +33 -0
- package/lib/connectors/schedule-listener.ts +207 -0
- package/lib/connectors/schedule-state-store.ts +54 -0
- package/lib/connectors/slack-adapter.ts +36 -0
- package/lib/{modules/connectors → connectors}/slack-connector-schema.ts +4 -1
- package/lib/{modules/connectors/funnel-slack-event-processor.ts → connectors/slack-event-processor.ts} +15 -9
- package/lib/{modules/connectors/funnel-slack-listener.ts → connectors/slack-listener.ts} +39 -14
- package/lib/engine/channels/channels.ts +520 -0
- package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +47 -62
- package/lib/engine/claude/gateway-controller.ts +4 -0
- package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +9 -0
- package/lib/{modules/fs/memory-funnel-file-system.ts → engine/fs/memory-file-system.ts} +20 -3
- package/lib/{modules/fs/node-funnel-file-system.ts → engine/fs/node-file-system.ts} +14 -2
- package/lib/{modules/http/memory-funnel-http-client.ts → engine/http/memory-http-client.ts} +1 -5
- package/lib/{modules/http/node-funnel-http-client.ts → engine/http/node-http-client.ts} +1 -5
- package/lib/engine/id/id-generator.ts +7 -0
- package/lib/engine/id/memory-id-generator.ts +20 -0
- package/lib/engine/id/node-id-generator.ts +7 -0
- package/lib/engine/logger/logger.ts +11 -0
- package/lib/engine/logger/memory-logger.ts +28 -0
- package/lib/engine/logger/node-logger.ts +49 -0
- package/lib/engine/logger/noop-logger.ts +9 -0
- package/lib/engine/mcp/channel-server.ts +204 -0
- package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +29 -10
- package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
- package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
- package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +5 -0
- package/lib/engine/profiles/profile-channel-checker.ts +7 -0
- package/lib/engine/profiles/profiles.ts +126 -0
- package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
- package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
- package/lib/engine/settings/settings-schema.ts +46 -0
- package/lib/engine/settings/settings-store.ts +110 -0
- package/lib/engine/time/clock.ts +15 -0
- package/lib/engine/time/memory-clock.ts +26 -0
- package/lib/engine/time/node-clock.ts +7 -0
- package/lib/funnel.ts +148 -56
- package/lib/gateway/auth-middleware.ts +44 -0
- package/lib/gateway/broadcaster.ts +319 -0
- package/lib/gateway/daemon.ts +47 -0
- package/lib/gateway/factory.ts +10 -0
- package/lib/gateway/funnel-event-store.ts +155 -0
- package/lib/gateway/gateway-server.ts +414 -0
- package/lib/gateway/gateway-token.ts +79 -0
- package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +70 -27
- package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +7 -3
- package/lib/gateway/listener-supervisor.ts +339 -0
- package/lib/gateway/listeners-client.ts +128 -0
- package/lib/gateway/resolve-daemon-script.ts +26 -0
- package/lib/gateway/routes/channels.connectors.call.ts +39 -0
- package/lib/gateway/routes/health.ts +13 -0
- package/lib/gateway/routes/index.ts +24 -0
- package/lib/gateway/routes/listeners.list.ts +6 -0
- package/lib/gateway/routes/listeners.restart.ts +15 -0
- package/lib/gateway/routes/listeners.start.ts +15 -0
- package/lib/gateway/routes/listeners.stop.ts +15 -0
- package/lib/gateway/routes/route-deps.ts +11 -0
- package/lib/gateway/routes/status.ts +15 -0
- package/lib/gateway/routes/validator.ts +17 -0
- package/lib/index.ts +50 -92
- package/lib/logger/leuco-human-file-writer.ts +65 -0
- package/lib/logger/leuco-human-logger.ts +98 -0
- package/lib/logger/leuco-human-record.ts +16 -0
- package/lib/logger/leuco-human-stdout-writer.ts +26 -0
- package/lib/logger/leuco-human-writer.ts +14 -0
- package/lib/logger/leuco-logger-memory-sink.ts +67 -0
- package/lib/logger/leuco-logger-record.ts +13 -0
- package/lib/logger/leuco-logger-sink.ts +33 -0
- package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
- package/lib/logger/leuco-logger.ts +135 -0
- package/lib/tui/app.tsx +357 -0
- package/lib/tui/components/add-row.tsx +18 -0
- package/lib/tui/components/brand.tsx +27 -0
- package/lib/tui/components/card.tsx +44 -0
- package/lib/tui/components/detail-bar.tsx +46 -0
- package/lib/tui/components/editable-field.tsx +33 -0
- package/lib/tui/components/empty-state.tsx +11 -0
- package/lib/tui/components/gateway-status.tsx +66 -0
- package/lib/tui/components/keymap.tsx +29 -0
- package/lib/tui/components/menu-item.tsx +73 -0
- package/lib/tui/components/menu.tsx +26 -0
- package/lib/tui/components/panel-header.tsx +22 -0
- package/lib/tui/components/readonly-field.tsx +18 -0
- package/lib/tui/components/section-header.tsx +25 -0
- package/lib/tui/components/selection-accent.tsx +32 -0
- package/lib/tui/components/session-item.tsx +33 -0
- package/lib/tui/components/session-list.tsx +33 -0
- package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
- package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
- package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
- package/lib/tui/components/ui/hascii/badge.tsx +51 -0
- package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
- package/lib/tui/components/ui/hascii/button.tsx +194 -0
- package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
- package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
- package/lib/tui/components/ui/hascii/card.tsx +27 -0
- package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
- package/lib/tui/components/ui/hascii/command.tsx +159 -0
- package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
- package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
- package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
- package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
- package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
- package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
- package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
- package/lib/tui/components/ui/hascii/input.tsx +130 -0
- package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
- package/lib/tui/components/ui/hascii/progress.tsx +28 -0
- package/lib/tui/components/ui/hascii/select.tsx +131 -0
- package/lib/tui/components/ui/hascii/separator.tsx +35 -0
- package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
- package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
- package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
- package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
- package/lib/tui/components/ui/hascii/slider.tsx +91 -0
- package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
- package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
- package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
- package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
- package/lib/tui/components/ui/hascii/switch.tsx +66 -0
- package/lib/tui/components/ui/hascii/table.tsx +95 -0
- package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
- package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
- package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
- package/lib/tui/components/ui/hascii/tree.tsx +104 -0
- package/lib/tui/components/view-shell.tsx +44 -0
- package/lib/tui/filter-input.tsx +33 -0
- package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
- package/lib/tui/parse-comma-list.ts +14 -0
- package/lib/tui/profile-launcher.tsx +61 -0
- package/lib/tui/scrollbar-options.ts +19 -0
- package/lib/tui/sidebar.tsx +50 -0
- package/lib/tui/theme.ts +40 -0
- package/lib/tui/tui.tsx +20 -0
- package/lib/tui/types.ts +38 -0
- package/lib/tui/unique-name.ts +18 -0
- package/lib/tui/use-event-stream.ts +133 -0
- package/lib/tui/use-snapshot.ts +99 -0
- package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
- package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
- package/lib/tui/utils/hascii/theme-context.tsx +26 -0
- package/lib/tui/utils/hascii/theme.ts +176 -0
- package/lib/tui/views/channels-view.tsx +108 -0
- package/lib/tui/views/connectors-view.tsx +164 -0
- package/lib/tui/views/events-view.tsx +160 -0
- package/lib/tui/views/listeners-view.tsx +80 -0
- package/lib/tui/views/profiles-view.tsx +152 -0
- package/package.json +51 -34
- package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
- package/lib/modules/channels/funnel-channels.ts +0 -155
- package/lib/modules/connectors/connector-config-schema.ts +0 -16
- package/lib/modules/connectors/connector-existence-checker.ts +0 -3
- package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
- package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
- package/lib/modules/connectors/funnel-connector-stores.ts +0 -24
- package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
- package/lib/modules/connectors/funnel-connectors.ts +0 -145
- package/lib/modules/connectors/funnel-discord-listener.ts +0 -65
- package/lib/modules/connectors/funnel-discord-store.ts +0 -84
- package/lib/modules/connectors/funnel-gh-store.ts +0 -84
- package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
- package/lib/modules/connectors/funnel-schedule-listener.ts +0 -124
- package/lib/modules/connectors/funnel-schedule-store.ts +0 -178
- package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
- package/lib/modules/connectors/funnel-slack-store.ts +0 -86
- package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -77
- package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
- package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
- package/lib/modules/gateway/daemon.ts +0 -207
- package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
- package/lib/modules/gateway/funnel-event-logger.ts +0 -59
- package/lib/modules/logger.ts +0 -26
- package/lib/modules/mcp/channel-server.ts +0 -76
- package/lib/modules/profiles/funnel-profiles.ts +0 -123
- package/lib/modules/profiles/profile-channel-checker.ts +0 -3
- package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
- package/lib/modules/repos/funnel-repositories.ts +0 -107
- package/lib/modules/schedule/funnel-schedule.ts +0 -34
- package/lib/modules/settings/funnel-settings-store.ts +0 -56
- package/lib/modules/settings/settings-schema.ts +0 -33
- package/lib/modules/tui/app.tsx +0 -44
- package/lib/modules/tui/tui.tsx +0 -13
- package/lib/routes/channels/add.help.ts +0 -3
- package/lib/routes/channels/add.ts +0 -21
- package/lib/routes/channels/connectors-attach.help.ts +0 -3
- package/lib/routes/channels/connectors-attach.ts +0 -17
- package/lib/routes/channels/connectors-detach.help.ts +0 -3
- package/lib/routes/channels/connectors-detach.ts +0 -17
- package/lib/routes/channels/group.help.ts +0 -16
- package/lib/routes/channels/group.ts +0 -22
- package/lib/routes/channels/remove.help.ts +0 -3
- package/lib/routes/channels/remove.ts +0 -17
- package/lib/routes/channels/rename.help.ts +0 -5
- package/lib/routes/channels/rename.ts +0 -17
- package/lib/routes/channels/routes.ts +0 -19
- package/lib/routes/channels/show.help.ts +0 -1
- package/lib/routes/channels/show.ts +0 -26
- package/lib/routes/claude/claude.help.ts +0 -16
- package/lib/routes/claude/claude.ts +0 -76
- package/lib/routes/claude/routes.ts +0 -4
- package/lib/routes/connectors/add.help.ts +0 -28
- package/lib/routes/connectors/add.ts +0 -64
- package/lib/routes/connectors/group.help.ts +0 -14
- package/lib/routes/connectors/group.ts +0 -18
- package/lib/routes/connectors/remove.help.ts +0 -3
- package/lib/routes/connectors/remove.ts +0 -17
- package/lib/routes/connectors/rename.help.ts +0 -5
- package/lib/routes/connectors/rename.ts +0 -17
- package/lib/routes/connectors/routes.ts +0 -23
- package/lib/routes/connectors/schedules-add.help.ts +0 -11
- package/lib/routes/connectors/schedules-add.ts +0 -33
- package/lib/routes/connectors/schedules-group.help.ts +0 -1
- package/lib/routes/connectors/schedules-group.ts +0 -38
- package/lib/routes/connectors/schedules-remove.help.ts +0 -3
- package/lib/routes/connectors/schedules-remove.ts +0 -17
- package/lib/routes/connectors/set.help.ts +0 -8
- package/lib/routes/connectors/set.ts +0 -72
- package/lib/routes/connectors/show.help.ts +0 -1
- package/lib/routes/connectors/show.ts +0 -41
- package/lib/routes/gateway/group.help.ts +0 -15
- package/lib/routes/gateway/group.ts +0 -28
- package/lib/routes/gateway/logs.help.ts +0 -13
- package/lib/routes/gateway/logs.ts +0 -100
- package/lib/routes/gateway/restart.help.ts +0 -10
- package/lib/routes/gateway/routes.ts +0 -18
- package/lib/routes/gateway/run.help.ts +0 -12
- package/lib/routes/gateway/run.ts +0 -35
- package/lib/routes/gateway/start.help.ts +0 -15
- package/lib/routes/gateway/start.ts +0 -32
- package/lib/routes/gateway/status.help.ts +0 -9
- package/lib/routes/gateway/status.ts +0 -28
- package/lib/routes/gateway/stop.help.ts +0 -8
- package/lib/routes/gateway/stop.ts +0 -21
- package/lib/routes/profiles/add.help.ts +0 -3
- package/lib/routes/profiles/add.ts +0 -33
- package/lib/routes/profiles/group.help.ts +0 -16
- package/lib/routes/profiles/group.ts +0 -25
- package/lib/routes/profiles/launch.help.ts +0 -4
- package/lib/routes/profiles/launch.ts +0 -36
- package/lib/routes/profiles/remove.help.ts +0 -3
- package/lib/routes/profiles/remove.ts +0 -17
- package/lib/routes/profiles/rename.help.ts +0 -5
- package/lib/routes/profiles/rename.ts +0 -17
- package/lib/routes/profiles/routes.ts +0 -18
- package/lib/routes/profiles/set.help.ts +0 -5
- package/lib/routes/profiles/set.ts +0 -32
- package/lib/routes/repos/add.help.ts +0 -6
- package/lib/routes/repos/add.ts +0 -20
- package/lib/routes/repos/group.help.ts +0 -11
- package/lib/routes/repos/group.ts +0 -18
- package/lib/routes/repos/remove.help.ts +0 -3
- package/lib/routes/repos/remove.ts +0 -17
- package/lib/routes/repos/rename.help.ts +0 -5
- package/lib/routes/repos/rename.ts +0 -17
- package/lib/routes/repos/routes.ts +0 -17
- package/lib/routes/repos/set.help.ts +0 -5
- package/lib/routes/repos/set.ts +0 -21
- package/lib/routes/repos/show.help.ts +0 -1
- package/lib/routes/repos/show.ts +0 -19
- package/lib/routes/request/discord-help.ts +0 -9
- package/lib/routes/request/discord.help.ts +0 -19
- package/lib/routes/request/discord.ts +0 -65
- package/lib/routes/request/group.help.ts +0 -15
- package/lib/routes/request/group.ts +0 -9
- package/lib/routes/request/routes.ts +0 -14
- package/lib/routes/request/slack-help.ts +0 -9
- package/lib/routes/request/slack.help.ts +0 -19
- package/lib/routes/request/slack.ts +0 -61
- package/lib/routes/status/routes.ts +0 -4
- package/lib/routes/status/status.help.ts +0 -6
- package/lib/routes/status/status.ts +0 -77
- package/lib/routes/update/routes.ts +0 -4
- package/lib/routes/update/update.help.ts +0 -5
- package/lib/routes/update/update.ts +0 -21
- package/lib/routes.ts +0 -40
- /package/lib/{factory.ts → cli/factory.ts} +0 -0
- /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
- /package/lib/{modules → cli}/router/validator.ts +0 -0
- /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
- /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
- /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,39 +1,33 @@
|
|
|
1
|
-
# @interactive-inc/claude-funnel
|
|
2
|
-
|
|
3
1
|
[](https://www.npmjs.com/package/@interactive-inc/claude-funnel)
|
|
4
2
|
[](./LICENSE)
|
|
5
3
|
|
|
6
|
-
A hub CLI that connects multiple Claude Code agents to external services (Slack / GitHub / Discord) and time-based triggers (cron). External events flow through subscription "channels" into Claude Code sessions, and outbound API calls from Claude are funneled through the same connectors.
|
|
4
|
+
A hub CLI that connects multiple Claude Code agents to external services (Slack / GitHub / Discord) and time-based triggers (cron). External events flow through subscription "channels" into Claude Code sessions, and outbound API calls from Claude are funneled through the same connectors as MCP tools.
|
|
7
5
|
|
|
8
6
|
The command is `funnel` or its shorthand `fnl`.
|
|
9
7
|
|
|
10
8
|
## Overview
|
|
11
9
|
|
|
12
10
|
```
|
|
13
|
-
External sources
|
|
14
|
-
(Slack / GitHub / Discord / cron)
|
|
15
|
-
│
|
|
16
|
-
▼
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Claude Code
|
|
29
|
-
(events surfaced as <channel> tags;
|
|
30
|
-
outbound calls go back through the
|
|
31
|
-
same connectors via funnel MCP)
|
|
11
|
+
External sources Outbound calls
|
|
12
|
+
(Slack / GitHub / Discord / cron) (Claude → MCP tools per connector)
|
|
13
|
+
│ ▲
|
|
14
|
+
▼ │
|
|
15
|
+
Channels (with nested per-type connectors)
|
|
16
|
+
│
|
|
17
|
+
▼ WebSocket
|
|
18
|
+
Gateway daemon
|
|
19
|
+
(port 9742: WS /ws + listener supervisor + reply API)
|
|
20
|
+
│
|
|
21
|
+
▼ MCP (stdio)
|
|
22
|
+
Claude Code
|
|
23
|
+
(events arrive as <channel> notifications;
|
|
24
|
+
one MCP tool is exposed per configured connector
|
|
25
|
+
so Claude can reply / send / call APIs without bash)
|
|
32
26
|
```
|
|
33
27
|
|
|
34
28
|
## Requirements
|
|
35
29
|
|
|
36
|
-
- [Bun](https://bun.sh) 1.3 or later (runtime)
|
|
30
|
+
- [Bun](https://bun.sh) 1.3 or later (runtime — used at install time to build the CLI bundle and at runtime to execute it)
|
|
37
31
|
- [Claude Code](https://docs.claude.com/en/docs/claude-code) CLI
|
|
38
32
|
- A Slack / GitHub / Discord token or CLI, depending on which connectors you use
|
|
39
33
|
|
|
@@ -43,123 +37,248 @@ External sources
|
|
|
43
37
|
bun add -g @interactive-inc/claude-funnel
|
|
44
38
|
```
|
|
45
39
|
|
|
46
|
-
After install, `funnel` and `fnl` are available globally.
|
|
40
|
+
`postinstall` runs `bun run build` to produce a single bundled CLI at `dist/bin.js`. After install, `funnel` and `fnl` are available globally.
|
|
47
41
|
|
|
48
42
|
## Quick start
|
|
49
43
|
|
|
50
44
|
```bash
|
|
51
|
-
#
|
|
52
|
-
fnl
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
fnl channels add my-inbox
|
|
56
|
-
fnl channels my-inbox connectors attach my-slack
|
|
45
|
+
# Create a subscription box (channel) and attach a connector
|
|
46
|
+
fnl channels add ops
|
|
47
|
+
fnl channels ops connectors add my-slack --type=slack \
|
|
48
|
+
--bot-token=xoxb-... --app-token=xapp-...
|
|
57
49
|
|
|
58
|
-
# Start the gateway (connects to Slack Socket Mode)
|
|
50
|
+
# Start the gateway (connects to Slack Socket Mode and surfaces events)
|
|
59
51
|
fnl gateway start
|
|
60
52
|
|
|
61
|
-
# Launch Claude
|
|
62
|
-
fnl claude --channel
|
|
53
|
+
# Launch Claude with a raw channel binding (no profile required)
|
|
54
|
+
fnl claude --channel ops
|
|
55
|
+
|
|
56
|
+
# Or save it as a profile and launch by name
|
|
57
|
+
fnl profiles add cto --path=/repo/myapp --sub-agent=cto --channel=ops
|
|
58
|
+
fnl claude --profile cto
|
|
63
59
|
```
|
|
64
60
|
|
|
65
61
|
Schedule (cron) trigger:
|
|
66
62
|
|
|
67
63
|
```bash
|
|
68
|
-
#
|
|
69
|
-
fnl connectors add daily --type
|
|
70
|
-
fnl connectors daily schedules add
|
|
71
|
-
|
|
72
|
-
# Attach it to a channel just like any other connector
|
|
73
|
-
fnl channels my-inbox connectors attach daily
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Commands
|
|
77
|
-
|
|
64
|
+
# A schedule connector contains many cron entries
|
|
65
|
+
fnl channels ops connectors add daily --type=schedule
|
|
66
|
+
fnl channels ops connectors daily schedules add morning \
|
|
67
|
+
--cron="0 9 * * *" --prompt="morning standup"
|
|
78
68
|
```
|
|
79
|
-
fnl connectors list
|
|
80
|
-
fnl connectors add <name> --type slack --bot-token xoxb-... --app-token xapp-...
|
|
81
|
-
fnl connectors add <name> --type gh [--poll-interval <sec>]
|
|
82
|
-
fnl connectors add <name> --type discord --bot-token <token>
|
|
83
|
-
fnl connectors add <name> --type schedule
|
|
84
|
-
fnl connectors <name> show details
|
|
85
|
-
fnl connectors <name> set [--bot-token ...] [--app-token ...] [--poll-interval ...]
|
|
86
|
-
fnl connectors rename <old> <new>
|
|
87
|
-
fnl connectors remove <name>
|
|
88
69
|
|
|
89
|
-
|
|
90
|
-
fnl connectors <name> schedules add --cron "<expr>" --prompt "<text>" [--disabled]
|
|
91
|
-
fnl connectors <name> schedules remove <id>
|
|
70
|
+
## CLI surface
|
|
92
71
|
|
|
93
|
-
|
|
94
|
-
fnl request discord <method> <path> [body] --connector <name> call Discord REST API
|
|
72
|
+
Connectors live nested inside their owning channel. Every CLI verb (`add` / `set` / `remove` / `rename` / `as-default` / `request`) maps to `POST` plus the verb in the URL — there is no method-stripping, so the same word stays visible in both shell and HTTP form. Read paths (no verb) stay `GET`.
|
|
95
73
|
|
|
96
|
-
|
|
97
|
-
fnl channels
|
|
98
|
-
fnl channels <name>
|
|
99
|
-
fnl channels <name>
|
|
100
|
-
fnl channels <name> connectors detach <connector>
|
|
101
|
-
fnl channels rename <old> <new>
|
|
74
|
+
```text
|
|
75
|
+
fnl channels list
|
|
76
|
+
fnl channels add <name> [--delivery=fanout|exclusive]
|
|
77
|
+
fnl channels <name> show details
|
|
102
78
|
fnl channels remove <name>
|
|
103
|
-
|
|
104
|
-
fnl
|
|
105
|
-
|
|
106
|
-
fnl
|
|
107
|
-
fnl
|
|
108
|
-
fnl
|
|
79
|
+
fnl channels rename <old> <new> (also `fnl channels <old> rename <new>`)
|
|
80
|
+
fnl channels <name> set delivery <mode>
|
|
81
|
+
|
|
82
|
+
fnl channels <ch> connectors list
|
|
83
|
+
fnl channels <ch> connectors add <c> --type=slack --bot-token=xoxb-... --app-token=xapp-...
|
|
84
|
+
fnl channels <ch> connectors add <c> --type=gh [--poll-interval=<sec>]
|
|
85
|
+
fnl channels <ch> connectors add <c> --type=discord --bot-token=<token>
|
|
86
|
+
fnl channels <ch> connectors add <c> --type=schedule
|
|
87
|
+
fnl channels <ch> connectors <c> show config
|
|
88
|
+
fnl channels <ch> connectors set <c> [--bot-token=...] [--app-token=...] [--poll-interval=...]
|
|
89
|
+
fnl channels <ch> connectors remove <c>
|
|
90
|
+
fnl channels <ch> connectors rename <c> <new>
|
|
91
|
+
fnl channels <ch> connectors <c> request --method=<api.method> [--key=value ...]
|
|
92
|
+
|
|
93
|
+
fnl channels <ch> connectors <c> schedules list cron entries
|
|
94
|
+
fnl channels <ch> connectors <c> schedules add <id> --cron="<expr>" --prompt="<text>" \
|
|
95
|
+
[--enabled=true] [--catchup-policy=latest|all|skip]
|
|
96
|
+
fnl channels <ch> connectors <c> schedules remove <id>
|
|
97
|
+
|
|
98
|
+
fnl profiles list (first entry is the default)
|
|
99
|
+
fnl profiles add <name> --path=<dir> --sub-agent=<agent> --channel=<channel-name>
|
|
100
|
+
fnl profiles <name> launch (alias for `<name> run`)
|
|
101
|
+
fnl profiles <name> run launch (sugar for `fnl claude --profile <name>`)
|
|
102
|
+
fnl profiles <name> set [--path=...] [--sub-agent=...] [--channel=...]
|
|
103
|
+
fnl profiles <name> as-default move to the front of the list
|
|
109
104
|
fnl profiles rename <old> <new>
|
|
110
105
|
fnl profiles remove <name>
|
|
111
106
|
|
|
112
|
-
fnl
|
|
113
|
-
fnl
|
|
114
|
-
fnl
|
|
115
|
-
fnl
|
|
116
|
-
fnl repos rename <old> <new>
|
|
117
|
-
fnl repos remove <name>
|
|
107
|
+
fnl claude launch the default profile
|
|
108
|
+
fnl claude --profile <name> launch a named profile
|
|
109
|
+
fnl claude --channel <name> raw launch (no profile, cwd = current dir)
|
|
110
|
+
fnl mcp run as an MCP server (invoked from .mcp.json)
|
|
118
111
|
|
|
119
|
-
fnl
|
|
120
|
-
fnl
|
|
121
|
-
fnl
|
|
122
|
-
|
|
123
|
-
fnl
|
|
112
|
+
fnl gateway status (default subcommand)
|
|
113
|
+
fnl gateway {start|stop|restart} daemon lifecycle
|
|
114
|
+
fnl gateway run foreground daemon (developer mode)
|
|
115
|
+
fnl gateway logs [-n <N>] tail diagnostic log
|
|
116
|
+
fnl gateway listeners live registry (alive / dead)
|
|
124
117
|
|
|
125
|
-
fnl gateway
|
|
126
|
-
fnl
|
|
127
|
-
fnl
|
|
128
|
-
fnl status overall status (connectors / channels / profiles / repos / gateway)
|
|
118
|
+
fnl status overall status (channels / profiles / gateway / clients)
|
|
119
|
+
fnl update `bun i -g @interactive-inc/claude-funnel`
|
|
120
|
+
fnl (no args) launch the OpenTUI dashboard
|
|
129
121
|
|
|
130
122
|
fnl --version
|
|
131
|
-
fnl --help
|
|
123
|
+
fnl --help every subcommand has --help; verb-without-arg also returns help
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--channel` accepts the channel **name** (not the uuid). The CLI resolves it to a channel id before calling the engine.
|
|
127
|
+
|
|
128
|
+
## Reply path: MCP tools per connector
|
|
129
|
+
|
|
130
|
+
When `fnl claude` launches Claude Code, the funnel MCP server connects to the gateway and reads the channel's connectors from `~/.funnel/settings.json`. For every callable connector (`slack` / `discord` / `gh`; `schedule` is one-way and skipped), the MCP advertises one tool with the connector's name. Claude can call them like:
|
|
131
|
+
|
|
132
|
+
```jsonc
|
|
133
|
+
// MCP: tools/list returns
|
|
134
|
+
{ "name": "discord", "inputSchema": { ... { method, path, body } ... } }
|
|
135
|
+
{ "name": "ops-slack", "inputSchema": { ... } }
|
|
136
|
+
{ "name": "gh-main", "inputSchema": { ... } }
|
|
137
|
+
|
|
138
|
+
// Claude calls
|
|
139
|
+
tools/call name="discord" arguments={
|
|
140
|
+
"method": "POST",
|
|
141
|
+
"path": "/channels/123/messages",
|
|
142
|
+
"body": { "content": "got it" }
|
|
143
|
+
}
|
|
132
144
|
```
|
|
133
145
|
|
|
146
|
+
The MCP forwards via HTTP `POST /channels/<channel>/connectors/<connector>/call` to the gateway daemon, which dispatches through the existing `FunnelChannels.call()` adapter. No bash subshell, no CLI cold start — replies are essentially synchronous.
|
|
147
|
+
|
|
148
|
+
If you need to invoke a connector from outside Claude, the same path is reachable as `fnl channels <ch> connectors <c> request --method=<...> [--key=value ...]`.
|
|
149
|
+
|
|
134
150
|
## Data model
|
|
135
151
|
|
|
136
152
|
```
|
|
137
|
-
|
|
153
|
+
Channel = { id, name, delivery, connectors[] }
|
|
154
|
+
subscription box; `delivery` is `fanout` (default; every WS client sees every event)
|
|
155
|
+
or `exclusive` (round-robin one client per event)
|
|
156
|
+
|
|
157
|
+
Connector =
|
|
138
158
|
| { type: "slack", name, botToken, appToken }
|
|
139
159
|
Slack Socket Mode
|
|
140
160
|
| { type: "gh", name, pollInterval? }
|
|
141
|
-
GitHub (gh CLI)
|
|
161
|
+
GitHub (gh CLI, poll-based)
|
|
142
162
|
| { type: "discord", name, botToken }
|
|
143
163
|
Discord Gateway
|
|
144
164
|
| { type: "schedule", name, entries[] }
|
|
145
|
-
cron-driven
|
|
165
|
+
cron-driven; entries = { id, cron, prompt, enabled?, catchupPolicy? }
|
|
146
166
|
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
Profile = { name, path, subAgent, channelId }
|
|
168
|
+
named launch preset; the first profile in the list is the default
|
|
149
169
|
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
Settings = { channels[], profiles[] }
|
|
171
|
+
→ ~/.funnel/settings.json
|
|
172
|
+
```
|
|
152
173
|
|
|
153
|
-
|
|
154
|
-
launch profile
|
|
174
|
+
Connectors are stored per type, one file per connector, under `~/.funnel/connectors/<type>/<name>.(json|jsonl)` so adding or retiring a type is contained to its own subdirectory.
|
|
155
175
|
|
|
156
|
-
|
|
157
|
-
→ ~/.funnel/settings.json
|
|
176
|
+
## Programmable API (Bun)
|
|
158
177
|
|
|
159
|
-
|
|
160
|
-
|
|
178
|
+
`funnel` is also usable as a library — the same `Funnel` facade the CLI uses is exported from the package root, with no CLI side effects.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { Funnel } from "@interactive-inc/claude-funnel"
|
|
182
|
+
|
|
183
|
+
const funnel = new Funnel() // defaults to ~/.funnel + the local filesystem
|
|
184
|
+
|
|
185
|
+
const channel = funnel.channels.add({ name: "inbox" })
|
|
186
|
+
|
|
187
|
+
funnel.channels.addConnector("inbox", {
|
|
188
|
+
type: "slack",
|
|
189
|
+
name: "my-slack",
|
|
190
|
+
botToken: "xoxb-...",
|
|
191
|
+
appToken: "xapp-...",
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
for (const c of funnel.channels.list()) console.log(c.name, c.connectors.length)
|
|
161
195
|
```
|
|
162
196
|
|
|
197
|
+
Every facet — `channels` / `profiles` / `gateway` / `gatewayServer` / `gatewayToken` / `listeners` / `mcp` / `claude` / `factory` / `store` / `process` / `logger` — is reachable from the same instance:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
funnel.gateway.getStatus() // { running, pid, port }
|
|
201
|
+
await funnel.gateway.start() // spawns the daemon as a separate process
|
|
202
|
+
|
|
203
|
+
await funnel.listeners.list() // talks to the running daemon over HTTP
|
|
204
|
+
await funnel.listeners.start("inbox", "my-slack")
|
|
205
|
+
await funnel.listeners.restart("inbox", "my-slack")
|
|
206
|
+
|
|
207
|
+
await funnel.claude.launch({ channel: "inbox" })
|
|
208
|
+
funnel.mcp.install("/path/to/repo") // writes .mcp.json
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Run the gateway in-process (no daemon spawn — useful for tests or embedding):
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const server = funnel.gatewayServer({ port: 9742 })
|
|
215
|
+
await server.start() // Bun.serve (HTTP + WS) + listener supervisor
|
|
216
|
+
const unsubscribe = server.getBroadcaster().subscribe(({ content, meta }) => {
|
|
217
|
+
console.log(meta?.connector, content)
|
|
218
|
+
})
|
|
219
|
+
await server.stop()
|
|
220
|
+
unsubscribe()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The gateway daemon exposes `/health`, `/status`, `/listeners*`, `/channels/:channel/connectors/:connector/call`, plus the `/ws?channel=<name>` WebSocket. There is no SPA; for a visual operator view, run `fnl` with no arguments to launch the OpenTUI dashboard.
|
|
224
|
+
|
|
225
|
+
Every side-effecting boundary is a DI seam. For tests / sandbox use, swap them all with the in-memory implementations and Funnel will not touch real disk, real processes, real time, or real UUIDs:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import {
|
|
229
|
+
Funnel,
|
|
230
|
+
MemoryFunnelClock,
|
|
231
|
+
MemoryFunnelFileSystem,
|
|
232
|
+
MemoryFunnelIdGenerator,
|
|
233
|
+
MemoryFunnelLogger,
|
|
234
|
+
MemoryFunnelProcessRunner,
|
|
235
|
+
MockFunnelSettingsReader,
|
|
236
|
+
} from "@interactive-inc/claude-funnel"
|
|
237
|
+
|
|
238
|
+
const funnel = new Funnel({
|
|
239
|
+
store: new MockFunnelSettingsReader(),
|
|
240
|
+
fs: new MemoryFunnelFileSystem(),
|
|
241
|
+
process: new MemoryFunnelProcessRunner(),
|
|
242
|
+
logger: new MemoryFunnelLogger(),
|
|
243
|
+
clock: new MemoryFunnelClock({ start: new Date("2026-01-01T00:00:00Z") }),
|
|
244
|
+
idGenerator: new MemoryFunnelIdGenerator({ prefix: "test" }),
|
|
245
|
+
dir: "/sandbox/.funnel",
|
|
246
|
+
tmpDir: "/sandbox/tmp",
|
|
247
|
+
})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Available abstractions (each has `Funnel*` interface, `Node*` default, and `Memory*` for tests): `FunnelFileSystem`, `FunnelProcessRunner`, `FunnelLogger`, `FunnelClock`, `FunnelIdGenerator`. Plus `NoopFunnelLogger` for silent operation.
|
|
251
|
+
|
|
252
|
+
The published package ships both TypeScript sources (under `lib/`) for library consumers and a single bundled CLI (`dist/bin.js`) which is what the `fnl` / `funnel` bin entries point to. Importing `@interactive-inc/claude-funnel/bin` resolves to the bundled CLI entry — only do this if you're embedding the CLI rather than the library.
|
|
253
|
+
|
|
254
|
+
## Claude Code skill
|
|
255
|
+
|
|
256
|
+
This repo ships a Claude Code skill at `.claude/skills/funnel/SKILL.md`. It briefs Claude on the architecture and command groups, and tells it to defer flag-level details to `funnel <command> --help`.
|
|
257
|
+
|
|
258
|
+
### Project-scoped (auto)
|
|
259
|
+
|
|
260
|
+
If you run `claude` inside this repo, the skill is picked up automatically — no install step.
|
|
261
|
+
|
|
262
|
+
### Global (use the skill in any project)
|
|
263
|
+
|
|
264
|
+
Claude Code does not currently provide a CLI to install skills from a remote URL, so copy the file into your personal skills directory:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# from a clone of this repo
|
|
268
|
+
mkdir -p ~/.claude/skills/funnel
|
|
269
|
+
cp .claude/skills/funnel/SKILL.md ~/.claude/skills/funnel/
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Or fetch it directly without cloning:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
mkdir -p ~/.claude/skills/funnel
|
|
276
|
+
curl -fsSL https://raw.githubusercontent.com/interactive-inc/open-claude-funnel/main/.claude/skills/funnel/SKILL.md \
|
|
277
|
+
-o ~/.claude/skills/funnel/SKILL.md
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
After this, Claude Code will load the skill in any session.
|
|
281
|
+
|
|
163
282
|
## Discord bot setup
|
|
164
283
|
|
|
165
284
|
- Create a bot in the Discord Developer Portal and obtain its token
|
|
@@ -168,39 +287,42 @@ Connectors are stored per type, one file per connector:
|
|
|
168
287
|
|
|
169
288
|
## Environment variables
|
|
170
289
|
|
|
171
|
-
| Variable
|
|
172
|
-
|
|
|
173
|
-
| `FUNNEL_CHANNEL_ID`
|
|
174
|
-
| `FUNNEL_PORT`
|
|
175
|
-
| `FUNNEL_GATEWAY_URL`
|
|
290
|
+
| Variable | Purpose |
|
|
291
|
+
| ---------------------- | --------------------------------------------------------------------------------------------- |
|
|
292
|
+
| `FUNNEL_CHANNEL_ID` | Injected into the child process by `fnl claude`; the funnel MCP uses it to subscribe. |
|
|
293
|
+
| `FUNNEL_PORT` | Gateway port (default 9742). |
|
|
294
|
+
| `FUNNEL_GATEWAY_URL` | Gateway base URL used by MCP for both WS subscribe and HTTP reply (default `http://localhost:9742`). |
|
|
295
|
+
| `FUNNEL_GATEWAY_TOKEN` | Bearer token for the gateway HTTP / WS. Defaults to the contents of `~/.funnel/gateway.token`. |
|
|
176
296
|
|
|
177
297
|
## File layout
|
|
178
298
|
|
|
179
|
-
- Config: `~/.funnel/settings.json` (channels
|
|
299
|
+
- Config: `~/.funnel/settings.json` (channels with nested connectors / profiles)
|
|
180
300
|
- Connectors: `~/.funnel/connectors/<type>/<name>.(json|jsonl)`
|
|
181
301
|
- `slack/<name>.json`, `gh/<name>.json`, `discord/<name>.json`
|
|
182
302
|
- `schedule/<name>.jsonl` (one entry per line) and `schedule/<name>.state.json` (last-fired timestamps for catch-up)
|
|
183
|
-
- PID: `~/.funnel/gateway.pid`
|
|
303
|
+
- Gateway PID: `~/.funnel/gateway.pid`, token: `~/.funnel/gateway.token`
|
|
184
304
|
- Claude PIDs: `~/.funnel/claude/<profile>.pid`
|
|
185
|
-
- Event
|
|
186
|
-
-
|
|
305
|
+
- Event store: `/tmp/funnel/events/events.db` (SQLite; broadcaster events with replay-by-seq)
|
|
306
|
+
- Diagnostic log: `/tmp/funnel/funnel.log` (gateway lifecycle, connect/disconnect, listener boot — what `funnel gateway logs` tails as YAML)
|
|
307
|
+
- Process log: `/tmp/funnel/gateway.log` (daemon stdout/stderr)
|
|
187
308
|
|
|
188
309
|
## Links
|
|
189
310
|
|
|
190
311
|
- [GitHub](https://github.com/interactive-inc/open-claude-funnel)
|
|
191
312
|
- [Issues](https://github.com/interactive-inc/open-claude-funnel/issues)
|
|
192
313
|
- Coding rules and design principles: [CLAUDE.md](https://github.com/interactive-inc/open-claude-funnel/blob/main/CLAUDE.md)
|
|
193
|
-
- Design notes: [`.docs/`](https://github.com/interactive-inc/open-claude-funnel/tree/main/.docs)
|
|
194
314
|
|
|
195
315
|
## Development
|
|
196
316
|
|
|
197
317
|
```bash
|
|
198
318
|
git clone https://github.com/interactive-inc/open-claude-funnel.git
|
|
199
|
-
cd claude-funnel
|
|
200
|
-
bun install
|
|
201
|
-
bun link #
|
|
319
|
+
cd open-claude-funnel
|
|
320
|
+
bun install # postinstall builds dist/bin.js
|
|
321
|
+
bun link # symlinks fnl / funnel → dist/bin.js
|
|
322
|
+
bun run build # rebuild after editing the cli
|
|
202
323
|
bun test # run tests
|
|
203
324
|
bunx tsc -b # type check
|
|
325
|
+
bun lib/bin.ts ... # run the cli from source (no build) for fast iteration
|
|
204
326
|
```
|
|
205
327
|
|
|
206
328
|
## License
|