tetra-cli 0.2.0__py3-none-any.whl

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 (62) hide show
  1. tetra_cli/__init__.py +6 -0
  2. tetra_cli/api_client/__init__.py +10 -0
  3. tetra_cli/api_client/client.py +173 -0
  4. tetra_cli/api_client/config.py +125 -0
  5. tetra_cli/api_client/operations/__init__.py +9 -0
  6. tetra_cli/api_client/operations/accounts.py +303 -0
  7. tetra_cli/api_client/operations/ai.py +278 -0
  8. tetra_cli/api_client/operations/analysis.py +190 -0
  9. tetra_cli/api_client/operations/api_keys.py +145 -0
  10. tetra_cli/api_client/operations/archive.py +114 -0
  11. tetra_cli/api_client/operations/awards.py +123 -0
  12. tetra_cli/api_client/operations/capacity.py +84 -0
  13. tetra_cli/api_client/operations/conversations.py +447 -0
  14. tetra_cli/api_client/operations/conversations_2.py +262 -0
  15. tetra_cli/api_client/operations/cosmetics.py +148 -0
  16. tetra_cli/api_client/operations/dashboard.py +282 -0
  17. tetra_cli/api_client/operations/data.py +250 -0
  18. tetra_cli/api_client/operations/events.py +734 -0
  19. tetra_cli/api_client/operations/gamification.py +470 -0
  20. tetra_cli/api_client/operations/goals.py +1144 -0
  21. tetra_cli/api_client/operations/groups.py +647 -0
  22. tetra_cli/api_client/operations/issues.py +198 -0
  23. tetra_cli/api_client/operations/offset.py +61 -0
  24. tetra_cli/api_client/operations/onboarding.py +284 -0
  25. tetra_cli/api_client/operations/outcome_schemas.py +292 -0
  26. tetra_cli/api_client/operations/peer_connections.py +243 -0
  27. tetra_cli/api_client/operations/plaid.py +329 -0
  28. tetra_cli/api_client/operations/reminders.py +273 -0
  29. tetra_cli/api_client/operations/scratches.py +280 -0
  30. tetra_cli/api_client/operations/skill_trees.py +160 -0
  31. tetra_cli/api_client/operations/social_2.py +560 -0
  32. tetra_cli/api_client/operations/social_3.py +618 -0
  33. tetra_cli/api_client/operations/social_4.py +527 -0
  34. tetra_cli/api_client/operations/strava.py +215 -0
  35. tetra_cli/api_client/operations/stripe.py +113 -0
  36. tetra_cli/api_client/operations/tags.py +488 -0
  37. tetra_cli/api_client/operations/values.py +867 -0
  38. tetra_cli/api_client/operations/values_2.py +584 -0
  39. tetra_cli/api_client/operations/watch.py +105 -0
  40. tetra_cli/api_client/operations/webhooks.py +50 -0
  41. tetra_cli/api_client/operations/xp.py +27 -0
  42. tetra_cli/cli/__init__.py +5 -0
  43. tetra_cli/cli/__main__.py +5 -0
  44. tetra_cli/cli/app.py +86 -0
  45. tetra_cli/cli/commands/__init__.py +1 -0
  46. tetra_cli/cli/commands/auth.py +201 -0
  47. tetra_cli/cli/commands/guide.py +8 -0
  48. tetra_cli/cli/commands/messages.py +161 -0
  49. tetra_cli/cli/commands/skill.py +71 -0
  50. tetra_cli/cli/context.py +13 -0
  51. tetra_cli/cli/generate.py +282 -0
  52. tetra_cli/cli/output.py +58 -0
  53. tetra_cli/mcp_gen.py +137 -0
  54. tetra_cli/ontology.py +70 -0
  55. tetra_cli/registry.py +118 -0
  56. tetra_cli/skill/SKILL.md +69 -0
  57. tetra_cli/skill/__init__.py +1 -0
  58. tetra_cli-0.2.0.dist-info/METADATA +140 -0
  59. tetra_cli-0.2.0.dist-info/RECORD +62 -0
  60. tetra_cli-0.2.0.dist-info/WHEEL +5 -0
  61. tetra_cli-0.2.0.dist-info/entry_points.txt +2 -0
  62. tetra_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,250 @@
1
+ """Pure async operations for the data export/import API.
2
+
3
+ These ops mirror the backend ``/api/v1/data`` routes (see
4
+ ``backend/tetra/api/routes/data.py``). Three of them — ``preview``,
5
+ ``import`` and ``import-mapped`` — accept a CSV/zip file as a
6
+ ``multipart/form-data`` upload, which the plain JSON-only ``TetraClient``
7
+ methods (``get``/``post``/…) cannot express. For those we go through the
8
+ shared ``_upload`` helper below, which drives the client's underlying httpx
9
+ ``AsyncClient`` with ``files=``/``data=`` and routes the response through the
10
+ same ``_handle_response`` normalizer every other op relies on. The helper is a
11
+ module-level function so unit tests can monkeypatch it and assert the exact
12
+ multipart payload (method, path, file name/bytes, form fields) without a live
13
+ backend.
14
+
15
+ The remaining routes (``fields``, ``fix-goal-statuses``) carry no body and use
16
+ the ordinary client methods directly.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json as _json
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from tetra_cli.api_client import TetraClient
25
+ from tetra_cli.api_client.client import _handle_response
26
+ from tetra_cli.registry import arg, operation, opt
27
+
28
+
29
+ async def _upload(
30
+ client: TetraClient,
31
+ path: str,
32
+ *,
33
+ file_field: str,
34
+ file_name: str,
35
+ file_bytes: bytes,
36
+ content_type: str,
37
+ data: dict[str, Any] | None = None,
38
+ ) -> Any:
39
+ """POST a ``multipart/form-data`` request through the client's httpx layer.
40
+
41
+ The JSON-oriented ``TetraClient.post`` only sends ``application/json``, so
42
+ file-upload routes can't use it. This helper reaches the client's
43
+ underlying ``AsyncClient`` (carrying the same auth + ``X-Tetra-Agent``
44
+ headers) to send a real multipart body, then normalizes the response with
45
+ the shared ``_handle_response`` so error handling matches every other op.
46
+
47
+ Args:
48
+ client: Authenticated TetraClient.
49
+ path: API path (e.g. ``/api/v1/data/import``).
50
+ file_field: The multipart field name the route expects (``file``).
51
+ file_name: File name to send (used by the server's extension check).
52
+ file_bytes: Raw file contents.
53
+ content_type: MIME type for the file part.
54
+ data: Extra form fields (e.g. ``mapping`` JSON, ``sample_size``).
55
+
56
+ Returns:
57
+ Parsed JSON response (or normalized error via ``_handle_response``).
58
+ """
59
+ files = {file_field: (file_name, file_bytes, content_type)}
60
+ response = await client._client.post(path, files=files, data=data or {})
61
+ return _handle_response(response)
62
+
63
+
64
+ def _read_file(file: str) -> tuple[str, bytes]:
65
+ """Read a local file, returning its base name and raw bytes.
66
+
67
+ Args:
68
+ file: Path to the local file to upload.
69
+
70
+ Returns:
71
+ ``(file_name, file_bytes)``.
72
+ """
73
+ p = Path(file)
74
+ return p.name, p.read_bytes()
75
+
76
+
77
+ @operation(
78
+ cli="data fields",
79
+ summary="List importable Tetra fields and the transforms available for mapping.",
80
+ covers=[("GET", "/api/v1/data/fields")],
81
+ )
82
+ async def get_field_definitions(client: TetraClient) -> dict[str, Any]:
83
+ """List the Tetra fields and transforms available for building a mapping.
84
+
85
+ Returns the same ``fields``/``transforms`` catalog the mapped-import flow
86
+ validates against, so an agent can construct a valid ``--mapping`` for
87
+ ``data import-mapped`` without guessing field or transform names.
88
+
89
+ Args:
90
+ client: Authenticated TetraClient.
91
+
92
+ Returns:
93
+ Dict with ``fields`` (per entity-type field defs) and ``transforms``
94
+ (transform name -> docstring).
95
+ """
96
+ return await client.get("/api/v1/data/fields")
97
+
98
+
99
+ @operation(
100
+ cli="data fix-goal-statuses",
101
+ summary="Transition CREATED goals that have events to IN_PROGRESS.",
102
+ covers=[("POST", "/api/v1/data/fix-goal-statuses")],
103
+ )
104
+ async def fix_goal_statuses(client: TetraClient) -> dict[str, Any]:
105
+ """Repair goal statuses: move CREATED goals with events to IN_PROGRESS.
106
+
107
+ A maintenance/no-body endpoint. Useful after a bulk import (or to fix
108
+ legacy data) where goals were never transitioned when their events landed.
109
+
110
+ Args:
111
+ client: Authenticated TetraClient.
112
+
113
+ Returns:
114
+ Detailed report dict: ``total_goals``, ``total_events``,
115
+ ``goals_transitioned`` (list of names), ``transitioned_count``, etc.
116
+ """
117
+ return await client.post("/api/v1/data/fix-goal-statuses")
118
+
119
+
120
+ @operation(
121
+ cli="data preview",
122
+ summary="Preview a CSV file's columns, sample rows, and mappable Tetra fields.",
123
+ covers=[("POST", "/api/v1/data/preview")],
124
+ params={
125
+ "file": arg(help="Path to the CSV file to preview"),
126
+ "sample_size": opt("--sample-size", min=1,
127
+ help="Number of sample rows to return (default 5)"),
128
+ },
129
+ )
130
+ async def preview_file(
131
+ client: TetraClient,
132
+ file: str,
133
+ *,
134
+ sample_size: int = 5,
135
+ ) -> dict[str, Any]:
136
+ """Preview a CSV file's structure to plan a mapped import.
137
+
138
+ Uploads the CSV (multipart) and returns its column headers, a handful of
139
+ sample rows, the row count, and the catalog of mappable Tetra fields and
140
+ transforms — everything needed to author a ``--mapping`` for
141
+ ``data import-mapped``.
142
+
143
+ Args:
144
+ client: Authenticated TetraClient.
145
+ file: Path to a local ``.csv`` file.
146
+ sample_size: How many sample rows to include (default 5).
147
+
148
+ Returns:
149
+ Dict with ``filename``, ``columns``, ``sample_rows``, ``row_count``,
150
+ ``tetra_fields`` and ``available_transforms``.
151
+ """
152
+ file_name, file_bytes = _read_file(file)
153
+ return await _upload(
154
+ client,
155
+ "/api/v1/data/preview",
156
+ file_field="file",
157
+ file_name=file_name,
158
+ file_bytes=file_bytes,
159
+ content_type="text/csv",
160
+ data={"sample_size": str(sample_size)},
161
+ )
162
+
163
+
164
+ @operation(
165
+ cli="data import",
166
+ summary="Import a Tetra export zip (values, goals, events CSVs).",
167
+ covers=[("POST", "/api/v1/data/import")],
168
+ params={
169
+ "file": arg(help="Path to the .zip export archive to import"),
170
+ },
171
+ )
172
+ async def import_data(
173
+ client: TetraClient,
174
+ file: str,
175
+ ) -> dict[str, Any]:
176
+ """Import a full Tetra export: a ``.zip`` of values/goals/events CSVs.
177
+
178
+ Mirrors the export format produced by ``data export``. The server imports
179
+ in dependency order (values -> goals -> events), skips entities that
180
+ already exist, transitions goals with events to IN_PROGRESS, and awards
181
+ retroactive XP. Only ``.zip`` archives are accepted.
182
+
183
+ Args:
184
+ client: Authenticated TetraClient.
185
+ file: Path to a local ``.zip`` archive.
186
+
187
+ Returns:
188
+ Dict with per-entity ``imported`` counts, optional ``skipped`` counts,
189
+ ``errors`` (or None), and ``success``.
190
+ """
191
+ file_name, file_bytes = _read_file(file)
192
+ return await _upload(
193
+ client,
194
+ "/api/v1/data/import",
195
+ file_field="file",
196
+ file_name=file_name,
197
+ file_bytes=file_bytes,
198
+ content_type="application/zip",
199
+ )
200
+
201
+
202
+ @operation(
203
+ cli="data import-mapped",
204
+ summary="Import a single CSV using a custom column->field mapping.",
205
+ covers=[("POST", "/api/v1/data/import-mapped")],
206
+ params={
207
+ "file": arg(help="Path to the CSV file to import"),
208
+ "mapping": opt("--mapping", json=True,
209
+ help=("MappingConfig as a JSON dict: entity_type, "
210
+ "column_mapping, and optional transforms/defaults/"
211
+ "value_maps/skip_if_not_empty/sign_modifiers.")),
212
+ },
213
+ )
214
+ async def import_mapped(
215
+ client: TetraClient,
216
+ file: str,
217
+ *,
218
+ mapping: dict[str, Any],
219
+ ) -> dict[str, Any]:
220
+ """Import one CSV into a chosen entity type via an explicit column mapping.
221
+
222
+ The backend expects ``mapping`` as a JSON string form field alongside the
223
+ file; this op accepts the mapping as a dict (the CLI parses the JSON blob)
224
+ and serializes it to JSON for the multipart ``mapping`` field. The dict
225
+ must carry at least ``entity_type`` ('values' | 'goals' | 'events') and
226
+ ``column_mapping`` ({source column -> tetra field(s)}); ``transforms``,
227
+ ``defaults``, ``value_maps``, ``skip_if_not_empty`` and ``sign_modifiers``
228
+ are optional. Use ``data fields`` / ``data preview`` to discover valid
229
+ field and transform names.
230
+
231
+ Args:
232
+ client: Authenticated TetraClient.
233
+ file: Path to a local ``.csv`` file.
234
+ mapping: MappingConfig dict (see above).
235
+
236
+ Returns:
237
+ Import result dict: ``imported``, ``total_rows``, ``errors`` (or None),
238
+ ``success`` and, when relevant, ``skipped`` / ``goals_transitioned`` /
239
+ XP fields.
240
+ """
241
+ file_name, file_bytes = _read_file(file)
242
+ return await _upload(
243
+ client,
244
+ "/api/v1/data/import-mapped",
245
+ file_field="file",
246
+ file_name=file_name,
247
+ file_bytes=file_bytes,
248
+ content_type="text/csv",
249
+ data={"mapping": _json.dumps(mapping)},
250
+ )