dailybot-cli 3.1.2__tar.gz → 3.2.0__tar.gz

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.
Files changed (50) hide show
  1. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/PKG-INFO +32 -2
  2. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/README.md +31 -1
  3. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/api_client.py +26 -0
  4. dailybot_cli-3.2.0/dailybot_cli/commands/conversation.py +187 -0
  5. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/form.py +17 -2
  6. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/user_scoped_actions.py +2 -0
  7. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/display.py +21 -0
  8. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/main.py +2 -0
  9. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/PKG-INFO +32 -2
  10. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/SOURCES.txt +1 -0
  11. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/pyproject.toml +1 -1
  12. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/LICENSE +0 -0
  13. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/__init__.py +0 -0
  14. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/__init__.py +0 -0
  15. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/agent.py +0 -0
  16. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/ask.py +0 -0
  17. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/auth.py +0 -0
  18. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/authoring_helpers.py +0 -0
  19. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/channels.py +0 -0
  20. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/chat.py +0 -0
  21. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/checkin.py +0 -0
  22. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/config.py +0 -0
  23. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/hook.py +0 -0
  24. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/identity.py +0 -0
  25. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/interactive.py +0 -0
  26. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/interactive_chat.py +0 -0
  27. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/kudos.py +0 -0
  28. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/public_api_helpers.py +0 -0
  29. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/query_options.py +0 -0
  30. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/status.py +0 -0
  31. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/team.py +0 -0
  32. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/uninstall.py +0 -0
  33. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/update.py +0 -0
  34. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/upgrade.py +0 -0
  35. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/user.py +0 -0
  36. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/version.py +0 -0
  37. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/workflow.py +0 -0
  38. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/config.py +0 -0
  39. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/install_method.py +0 -0
  40. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/ledger.py +0 -0
  41. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/__init__.py +0 -0
  42. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/app.py +0 -0
  43. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/commands.py +0 -0
  44. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/conversation.py +0 -0
  45. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/intents.py +0 -0
  46. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/dependency_links.txt +0 -0
  47. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/entry_points.txt +0 -0
  48. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/requires.txt +0 -0
  49. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/top_level.txt +0 -0
  50. {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dailybot-cli
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more.
5
5
  Author-email: Dailybot <support@dailybot.com>
6
6
  License: MIT
@@ -877,7 +877,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
877
877
 
878
878
  | Command | Description |
879
879
  |---------|-------------|
880
- | `dailybot form list` | List forms visible to you (includes question count) |
880
+ | `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
881
881
  | `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
882
882
  | `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
883
883
  | `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
@@ -923,6 +923,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
923
923
  |---------|-------------|
924
924
  | `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
925
925
  | `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
926
+ | `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
926
927
 
927
928
  ```bash
928
929
  # To a channel
@@ -984,6 +985,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
984
985
  outside your role's reach returns a clear error, as does the per-token CLI rate
985
986
  limit.
986
987
 
988
+ ### Conversation commands (Slack group DMs)
989
+
990
+ | Command | Description |
991
+ |---------|-------------|
992
+ | `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
993
+
994
+ ```bash
995
+ # Open a group DM with two teammates (by UUID) — prints the channel id
996
+ dailybot conversation open -u <uuid1> -u <uuid2>
997
+
998
+ # Name or email work too (resolved via the org directory)
999
+ dailybot conversation open -u "Mauricio Gomez"
1000
+ dailybot conversation open --emails ana@co.com,luis@co.com
1001
+
1002
+ # Open the group and immediately post a report in one call
1003
+ dailybot conversation open -u "Mauricio Gomez" \
1004
+ -m "Report on the latest analysis — see thread for details."
1005
+
1006
+ # Headless: capture the channel id for scripting
1007
+ CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
1008
+ ```
1009
+
1010
+ **Slack only, org-admin only.** Opening is **idempotent** — the same set of
1011
+ participants always returns the same channel, so "open it, or reuse the existing
1012
+ one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
1013
+ get a clear "Slack only" message; non-admins get a permissions error. The
1014
+ optional `-m/--message` chains a `group_chat` send to the returned channel (the
1015
+ same delivery path as `chat send`).
1016
+
987
1017
  ### Agent commands
988
1018
 
989
1019
  | Command | Description |
@@ -838,7 +838,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
838
838
 
839
839
  | Command | Description |
840
840
  |---------|-------------|
841
- | `dailybot form list` | List forms visible to you (includes question count) |
841
+ | `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
842
842
  | `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
843
843
  | `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
844
844
  | `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
@@ -884,6 +884,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
884
884
  |---------|-------------|
885
885
  | `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
886
886
  | `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
887
+ | `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
887
888
 
888
889
  ```bash
889
890
  # To a channel
@@ -945,6 +946,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
945
946
  outside your role's reach returns a clear error, as does the per-token CLI rate
946
947
  limit.
947
948
 
949
+ ### Conversation commands (Slack group DMs)
950
+
951
+ | Command | Description |
952
+ |---------|-------------|
953
+ | `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
954
+
955
+ ```bash
956
+ # Open a group DM with two teammates (by UUID) — prints the channel id
957
+ dailybot conversation open -u <uuid1> -u <uuid2>
958
+
959
+ # Name or email work too (resolved via the org directory)
960
+ dailybot conversation open -u "Mauricio Gomez"
961
+ dailybot conversation open --emails ana@co.com,luis@co.com
962
+
963
+ # Open the group and immediately post a report in one call
964
+ dailybot conversation open -u "Mauricio Gomez" \
965
+ -m "Report on the latest analysis — see thread for details."
966
+
967
+ # Headless: capture the channel id for scripting
968
+ CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
969
+ ```
970
+
971
+ **Slack only, org-admin only.** Opening is **idempotent** — the same set of
972
+ participants always returns the same channel, so "open it, or reuse the existing
973
+ one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
974
+ get a clear "Slack only" message; non-admins get a permissions error. The
975
+ optional `-m/--message` chains a `group_chat` send to the returned channel (the
976
+ same delivery path as `chat send`).
977
+
948
978
  ### Agent commands
949
979
 
950
980
  | Command | Description |
@@ -795,6 +795,7 @@ class DailyBotClient:
795
795
  *,
796
796
  include_questions: bool = False,
797
797
  include_archived: bool = False,
798
+ owner: str | None = None,
798
799
  search: str | None = None,
799
800
  start_date: str | None = None,
800
801
  end_date: str | None = None,
@@ -806,6 +807,9 @@ class DailyBotClient:
806
807
  ) -> list[dict[str, Any]]:
807
808
  """GET /v1/forms/ — optionally expand questions, search, and page.
808
809
 
810
+ The default response is org-scoped (every form the caller can see on the
811
+ webapp list view). Pass ``owner="me"`` to narrow to the caller's own forms.
812
+
809
813
  When ``meta`` is given it is populated with ``count`` / ``next`` for a
810
814
  pagination footer.
811
815
  """
@@ -814,6 +818,8 @@ class DailyBotClient:
814
818
  params["include"] = "questions"
815
819
  if include_archived:
816
820
  params["include_archived"] = "true"
821
+ if owner:
822
+ params["owner"] = owner
817
823
  _merge_list_query(params, search=search, start_date=start_date, end_date=end_date)
818
824
  result: PaginatedResult = self._paginated_get(
819
825
  f"{self.api_url}/v1/forms/",
@@ -1493,6 +1499,26 @@ class DailyBotClient:
1493
1499
  )
1494
1500
  return self._handle_response(response)
1495
1501
 
1502
+ def open_conversation(self, users_uuids: list[str]) -> dict[str, Any]:
1503
+ """POST /v1/open-conversation/ — open (or fetch) a Slack group DM (MPIM).
1504
+
1505
+ Opens a private Slack group conversation that includes the given org
1506
+ users plus the Dailybot bot (Slack adds the bot because the call uses the
1507
+ org's bot token). The call is idempotent: the same set of users returns
1508
+ the same channel if it already exists.
1509
+
1510
+ Slack-only and org-admin-only, both server-enforced. Authenticated via
1511
+ the shared agent header logic (``X-API-KEY`` preferred, else the login
1512
+ Bearer token). Returns ``{"channel": "<slack-conversation-id>"}``.
1513
+ """
1514
+ response: httpx.Response = httpx.post(
1515
+ f"{self.api_url}/v1/open-conversation/",
1516
+ json={"users_uuids": users_uuids},
1517
+ headers=self._agent_headers(),
1518
+ timeout=self.timeout,
1519
+ )
1520
+ return self._handle_response(response)
1521
+
1496
1522
  # --- Agent message endpoints ---
1497
1523
 
1498
1524
  def send_agent_message(
@@ -0,0 +1,187 @@
1
+ """``conversation`` command group — open a Slack group DM (MPIM) that includes
2
+ the Dailybot bot, and optionally post a message to it.
3
+
4
+ Wraps ``POST /v1/open-conversation/`` (Slack-only, org-admin-only, idempotent)
5
+ and chains the existing ``POST /v1/send-message/`` path with
6
+ ``channel_type=group_chat`` when ``--message`` is given. Authenticated with a
7
+ login Bearer token or an org API key. Participants can be named by UUID, email,
8
+ or name — non-UUID identifiers are resolved client-side via ``GET /v1/users/``.
9
+ """
10
+
11
+ from typing import Any, NoReturn
12
+
13
+ import click
14
+
15
+ from dailybot_cli.api_client import APIError, DailyBotClient
16
+ from dailybot_cli.commands.public_api_helpers import (
17
+ UUID_PATTERN,
18
+ emit_json,
19
+ require_auth,
20
+ resolve_user_by_name_or_uuid,
21
+ )
22
+ from dailybot_cli.display import (
23
+ console,
24
+ print_conversation_result,
25
+ print_error,
26
+ )
27
+
28
+ GROUP_CHAT_TYPE: str = "group_chat"
29
+
30
+
31
+ def _split_identifiers(values: tuple[str, ...]) -> list[str]:
32
+ """Flatten repeatable and comma-separated option values into a clean list."""
33
+ out: list[str] = []
34
+ for value in values:
35
+ out.extend(part.strip() for part in value.split(",") if part.strip())
36
+ return out
37
+
38
+
39
+ def _resolve_participants(client: DailyBotClient, identifiers: list[str]) -> list[tuple[str, str]]:
40
+ """Resolve each identifier (UUID, email, or name) to a (uuid, name) pair.
41
+
42
+ The user directory is fetched only when at least one identifier is not a bare
43
+ UUID, so an all-UUID call avoids the extra round-trip. Order is preserved and
44
+ duplicate UUIDs are dropped (keeping the first occurrence).
45
+ """
46
+ needs_directory: bool = any(not UUID_PATTERN.match(ident) for ident in identifiers)
47
+ directory: list[dict[str, Any]] = (
48
+ client.list_users(include_email=True) if needs_directory else []
49
+ )
50
+ resolved: list[tuple[str, str]] = []
51
+ seen: set[str] = set()
52
+ for ident in identifiers:
53
+ uuid, name = resolve_user_by_name_or_uuid(directory, ident)
54
+ if uuid in seen:
55
+ continue
56
+ seen.add(uuid)
57
+ resolved.append((uuid, name))
58
+ return resolved
59
+
60
+
61
+ def _exit_for_conversation_error(exc: APIError, json_mode: bool) -> NoReturn:
62
+ """Translate an open-conversation / send failure to a friendly message + exit."""
63
+ code: str | None = getattr(exc, "code", None)
64
+ if code == "open_conversation_not_supported" or exc.status_code == 406:
65
+ message: str = "Group conversations are only supported for Slack workspaces."
66
+ elif code == "one_or_more_users_not_found":
67
+ message = "One or more users were not found or aren't active in your organization."
68
+ elif code == "no_valid_users":
69
+ message = "The participant list contains invalid user UUIDs."
70
+ elif code == "conversation_can_not_be_opened":
71
+ message = "Slack rejected the request. A participant may be deactivated on the Slack side."
72
+ elif exc.status_code == 403:
73
+ message = "This command requires organization admin privileges."
74
+ elif exc.status_code == 401:
75
+ message = "Session expired. Run: dailybot login"
76
+ elif exc.status_code == 429:
77
+ message = "Rate limit exceeded. Wait a moment and try again."
78
+ else:
79
+ message = exc.detail
80
+ if json_mode:
81
+ payload: dict[str, Any] = {"error": message, "status": exc.status_code}
82
+ if code:
83
+ payload["code"] = code
84
+ emit_json(payload)
85
+ else:
86
+ print_error(message)
87
+ raise SystemExit(1)
88
+
89
+
90
+ @click.group(name="conversation")
91
+ def conversation() -> None:
92
+ """Open Slack group DMs (with the Dailybot bot) and post to them.
93
+
94
+ \b
95
+ Slack only, organization admin only. Opens a private group DM (MPIM) that
96
+ includes the Dailybot bot plus the users you name, then optionally posts a
97
+ message. Opening is idempotent — the same set of users returns the same
98
+ channel.
99
+ """
100
+
101
+
102
+ @conversation.command("open")
103
+ @click.option(
104
+ "--user",
105
+ "--users",
106
+ "-u",
107
+ "users_raw",
108
+ multiple=True,
109
+ help="Participant by UUID, email, or name (repeatable or comma-separated).",
110
+ )
111
+ @click.option(
112
+ "--email",
113
+ "--emails",
114
+ "-e",
115
+ "emails_raw",
116
+ multiple=True,
117
+ help="Participant by email (repeatable or comma-separated); resolved to a UUID.",
118
+ )
119
+ @click.option(
120
+ "--message",
121
+ "-m",
122
+ default=None,
123
+ help="Optional message to post to the group right after opening it.",
124
+ )
125
+ @click.option("--json", "json_mode", is_flag=True, help="Emit machine-readable JSON to stdout.")
126
+ def conversation_open(
127
+ users_raw: tuple[str, ...],
128
+ emails_raw: tuple[str, ...],
129
+ message: str | None,
130
+ json_mode: bool,
131
+ ) -> None:
132
+ """Open (or fetch) a Slack group DM and optionally post a message.
133
+
134
+ \b
135
+ Slack only, org admin only. Idempotent — the same participants return the
136
+ same channel. Participants accept UUID, email, or name; --emails is a
137
+ convenience alias that only takes emails.
138
+
139
+ \b
140
+ Examples:
141
+ dailybot conversation open -u <uuid1> -u <uuid2>
142
+ dailybot conversation open -u Mauricio -m "Report on the latest analysis"
143
+ dailybot conversation open --emails ana@co.com,luis@co.com --json
144
+ """
145
+ identifiers: list[str] = _split_identifiers(users_raw) + _split_identifiers(emails_raw)
146
+ if not identifiers:
147
+ print_error("Provide at least one participant with --user (UUID, email, or name).")
148
+ raise SystemExit(1)
149
+
150
+ client: DailyBotClient = require_auth()
151
+ try:
152
+ participants: list[tuple[str, str]] = _resolve_participants(client, identifiers)
153
+ except ValueError as exc:
154
+ print_error(str(exc))
155
+ raise SystemExit(1)
156
+
157
+ uuids: list[str] = [uuid for uuid, _name in participants]
158
+ try:
159
+ with console.status("Opening group conversation..."):
160
+ result: dict[str, Any] = client.open_conversation(uuids)
161
+ except APIError as exc:
162
+ _exit_for_conversation_error(exc, json_mode)
163
+
164
+ channel: str = str(result.get("channel", ""))
165
+ message_sent: bool = False
166
+ if message:
167
+ payload: dict[str, Any] = {
168
+ "message": message,
169
+ "target_channels": [{"id": channel, "channel_type": GROUP_CHAT_TYPE}],
170
+ }
171
+ try:
172
+ with console.status("Sending message..."):
173
+ client.send_chat_message(payload)
174
+ except APIError as exc:
175
+ _exit_for_conversation_error(exc, json_mode)
176
+ message_sent = True
177
+
178
+ if json_mode:
179
+ emit_json(
180
+ {
181
+ "channel": channel,
182
+ "participants": [{"uuid": uuid, "name": name} for uuid, name in participants],
183
+ "message_sent": message_sent,
184
+ }
185
+ )
186
+ return
187
+ print_conversation_result(channel, participants, message_sent=message_sent)
@@ -188,10 +188,16 @@ def form() -> None:
188
188
  is_flag=True,
189
189
  help="Include archived forms (hidden by default).",
190
190
  )
191
+ @click.option(
192
+ "--mine",
193
+ is_flag=True,
194
+ help="Only forms you own (default lists every form you can see in the org).",
195
+ )
191
196
  @query_options
192
197
  @click.option("--json", "json_mode", is_flag=True, help="Emit machine-readable JSON to stdout.")
193
198
  def form_list(
194
199
  include_archived: bool,
200
+ mine: bool,
195
201
  json_mode: bool,
196
202
  page: int | None,
197
203
  page_size: int | None,
@@ -207,12 +213,15 @@ def form_list(
207
213
  """List forms visible to you.
208
214
 
209
215
  \b
210
- Acts as you. You can only see and act on what you could in the webapp.
216
+ Acts as you. You can only see and act on what you could in the webapp:
217
+ by default this is every form in your org you have list-view access to.
218
+ Pass --mine to narrow to only the forms you own.
211
219
  Archived forms are hidden unless you pass --include-archived.
212
220
 
213
221
  \b
214
222
  Examples:
215
223
  dailybot form list
224
+ dailybot form list --mine
216
225
  dailybot form list --search retro --since 2026-07-01
217
226
  dailybot form list --all
218
227
  dailybot form list --json
@@ -235,7 +244,13 @@ def form_list(
235
244
  print_error(str(exc))
236
245
  raise SystemExit(1)
237
246
  client = require_auth()
238
- execute_form_list(client, json_mode=json_mode, include_archived=include_archived, spec=spec)
247
+ execute_form_list(
248
+ client,
249
+ json_mode=json_mode,
250
+ include_archived=include_archived,
251
+ owner="me" if mine else None,
252
+ spec=spec,
253
+ )
239
254
 
240
255
 
241
256
  @form.command("get")
@@ -500,6 +500,7 @@ def execute_form_list(
500
500
  *,
501
501
  json_mode: bool = False,
502
502
  include_archived: bool = False,
503
+ owner: str | None = None,
503
504
  spec: QuerySpec | None = None,
504
505
  ) -> list[dict[str, Any]] | None:
505
506
  """Fetch and display forms visible to the user (with question counts)."""
@@ -511,6 +512,7 @@ def execute_form_list(
511
512
  forms: list[dict[str, Any]] = client.list_forms(
512
513
  include_questions=True,
513
514
  include_archived=include_archived,
515
+ owner=owner,
514
516
  search=query.params.get("search"),
515
517
  start_date=query.params.get("start_date"),
516
518
  end_date=query.params.get("end_date"),
@@ -406,6 +406,27 @@ def print_chat_message_result(data: dict[str, Any], *, updated: bool = False) ->
406
406
  console.print(Panel(table, title=f"[bold]{title}[/bold]", border_style="green"))
407
407
 
408
408
 
409
+ def print_conversation_result(
410
+ channel: str,
411
+ participants: list[tuple[str, str]],
412
+ *,
413
+ message_sent: bool = False,
414
+ ) -> None:
415
+ """Display the opened Slack group conversation and its participants.
416
+
417
+ ``participants`` is a list of ``(uuid, display_name)`` pairs. ``message_sent``
418
+ flags whether a message was posted to the group right after opening it.
419
+ """
420
+ table: Table = Table(show_header=False, box=None, padding=(0, 2))
421
+ table.add_column(style="bold cyan", no_wrap=True)
422
+ table.add_column()
423
+ table.add_row("Channel", channel)
424
+ table.add_row("Participants", ", ".join(name for _uuid, name in participants) or "[dim]—[/dim]")
425
+ if message_sent:
426
+ table.add_row("Message", "Sent to the group")
427
+ console.print(Panel(table, title="[bold]Group Conversation[/bold]", border_style="green"))
428
+
429
+
409
430
  def print_agent_report_result(result: dict[str, Any]) -> None:
410
431
  """Display the result of submitting an agent activity report.
411
432
 
@@ -12,6 +12,7 @@ from dailybot_cli.commands.channels import channels
12
12
  from dailybot_cli.commands.chat import chat
13
13
  from dailybot_cli.commands.checkin import checkin
14
14
  from dailybot_cli.commands.config import config
15
+ from dailybot_cli.commands.conversation import conversation
15
16
  from dailybot_cli.commands.form import form
16
17
  from dailybot_cli.commands.hook import hook
17
18
  from dailybot_cli.commands.identity import me, org
@@ -86,6 +87,7 @@ cli.add_command(org)
86
87
  cli.add_command(workflow)
87
88
  cli.add_command(agent)
88
89
  cli.add_command(chat)
90
+ cli.add_command(conversation)
89
91
  cli.add_command(ask)
90
92
  cli.add_command(interactive)
91
93
  cli.add_command(config)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dailybot-cli
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more.
5
5
  Author-email: Dailybot <support@dailybot.com>
6
6
  License: MIT
@@ -877,7 +877,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
877
877
 
878
878
  | Command | Description |
879
879
  |---------|-------------|
880
- | `dailybot form list` | List forms visible to you (includes question count) |
880
+ | `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
881
881
  | `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
882
882
  | `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
883
883
  | `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
@@ -923,6 +923,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
923
923
  |---------|-------------|
924
924
  | `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
925
925
  | `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
926
+ | `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
926
927
 
927
928
  ```bash
928
929
  # To a channel
@@ -984,6 +985,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
984
985
  outside your role's reach returns a clear error, as does the per-token CLI rate
985
986
  limit.
986
987
 
988
+ ### Conversation commands (Slack group DMs)
989
+
990
+ | Command | Description |
991
+ |---------|-------------|
992
+ | `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
993
+
994
+ ```bash
995
+ # Open a group DM with two teammates (by UUID) — prints the channel id
996
+ dailybot conversation open -u <uuid1> -u <uuid2>
997
+
998
+ # Name or email work too (resolved via the org directory)
999
+ dailybot conversation open -u "Mauricio Gomez"
1000
+ dailybot conversation open --emails ana@co.com,luis@co.com
1001
+
1002
+ # Open the group and immediately post a report in one call
1003
+ dailybot conversation open -u "Mauricio Gomez" \
1004
+ -m "Report on the latest analysis — see thread for details."
1005
+
1006
+ # Headless: capture the channel id for scripting
1007
+ CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
1008
+ ```
1009
+
1010
+ **Slack only, org-admin only.** Opening is **idempotent** — the same set of
1011
+ participants always returns the same channel, so "open it, or reuse the existing
1012
+ one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
1013
+ get a clear "Slack only" message; non-admins get a permissions error. The
1014
+ optional `-m/--message` chains a `group_chat` send to the returned channel (the
1015
+ same delivery path as `chat send`).
1016
+
987
1017
  ### Agent commands
988
1018
 
989
1019
  | Command | Description |
@@ -23,6 +23,7 @@ dailybot_cli/commands/channels.py
23
23
  dailybot_cli/commands/chat.py
24
24
  dailybot_cli/commands/checkin.py
25
25
  dailybot_cli/commands/config.py
26
+ dailybot_cli/commands/conversation.py
26
27
  dailybot_cli/commands/form.py
27
28
  dailybot_cli/commands/hook.py
28
29
  dailybot_cli/commands/identity.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dailybot-cli"
7
- version = "3.1.2"
7
+ version = "3.2.0"
8
8
  description = "Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes