typefully-cli 0.1.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.
Binary file
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: pip install -e ".[dev]"
26
+
27
+ - name: Lint
28
+ run: ruff check src/ tests/
29
+
30
+ - name: Test
31
+ run: python -m pytest tests/ -v
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+ .venv/
9
+ venv/
10
+ .env
11
+ .ruff_cache/
12
+ .pytest_cache/
13
+ .mypy_cache/
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-03-26)
4
+
5
+ Initial release.
6
+
7
+ ### Commands
8
+ - `config set`, `config show`, `config path` -- manage configuration
9
+ - `me` -- show authenticated user
10
+ - `accounts`, `account-detail` -- list and inspect connected social sets
11
+ - `draft`, `thread` -- create single posts and multi-post threads
12
+ - `get`, `update`, `delete` -- manage existing drafts
13
+ - `drafts` -- list drafts with filtering (status, tag, limit, offset, order)
14
+ - `recent` -- list published posts with date range pagination
15
+ - `tags`, `tag-create` -- list and create tags (auto-created via `--tag`)
16
+ - `upload` -- upload media files (S3 presigned URL + poll)
17
+ - `batch` -- batch create drafts/threads from text file with dry-run support
18
+
19
+ ### Features
20
+ - Agent-first JSON output by default, `--text` for human-readable Rich output
21
+ - Structured error JSON on stderr with exit codes (0/1/2/3)
22
+ - Auth chain: `--api-key` flag > `TYPEFULLY_API_KEY` env var > config file > 1Password
23
+ - Account resolution by name, username, or numeric ID
24
+ - Partial failure handling for delete and batch (exit 3 with both streams)
25
+ - Cross-platform posting: X, LinkedIn, Threads, Bluesky, Mastodon
26
+ - Media filename sanitization (spaces/special chars replaced with `_`)
27
+ - Rate limiting on batch operations (0.3s between requests)
28
+
29
+ ### QA fixes
30
+ - Rich markup escaping for all user/API content in `--text` mode
31
+ - Date validation for `--since` / `--until` (YYYY-MM-DD required)
32
+ - Local limit validation (1-50) for `drafts`, `tags`, `recent`
33
+ - Clean error payloads in partial-failure stdout (no raw upstream bodies)
34
+ - README accuracy: limit caps, date validation, tag/media lifecycle scope
@@ -0,0 +1,59 @@
1
+ # typefully-cli
2
+
3
+ ## Overview
4
+
5
+ Python CLI wrapping the Typefully API v2. Agent-first design: JSON output by default, structured errors on stderr, deterministic behavior. 17 Click commands for drafts, threads, batch, media, tags, accounts, config.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ src/typefully_cli/
11
+ cli.py -- 17 Click commands, shared options, error handler, payload builder
12
+ client.py -- httpx wrapper over Typefully API v2 (rate-limited, account resolution)
13
+ auth.py -- API key resolution chain: flag > env > config > 1Password
14
+ config.py -- TOML config at ~/.config/typefully/config.toml
15
+ console.py -- stderr output (status, warnings, errors) via Rich
16
+ output.py -- Rich --text formatting (all user content escaped via rich.markup.escape)
17
+ batch.py -- Batch file parser (--- separators, === threads, metadata headers)
18
+ media.py -- S3 presigned upload + poll workflow
19
+ exceptions.py -- TypefullyError hierarchy with structured JSON serialization
20
+ ```
21
+
22
+ ## Key patterns
23
+
24
+ - **Output contract:** Success -> `{"ok": true, "data": ...}` on stdout. Errors -> `{"ok": false, "error": {...}}` on stderr. Exit codes: 0 success, 1 input/auth, 2 API, 3 partial failure.
25
+ - **Rich markup safety:** All user/API text in output.py is wrapped with `_esc()` (rich.markup.escape) before printing. Never pass raw API content to `_console.print()`.
26
+ - **Input validation:** Dates via `_validate_date()`, limits via `_validate_limit()`, both raise `TypefullyError(code="invalid_input")` which exits 1.
27
+ - **Partial failure:** Delete and batch loops catch per-item errors. `_handle_multi_result()` handles the 3-branch contract (all succeed / partial / all fail).
28
+ - **Error sanitization:** `APIError.user_message` property strips raw upstream bodies from stdout payloads. Full details stay on stderr.
29
+
30
+ ## Dev setup
31
+
32
+ ```bash
33
+ pip install -e ".[dev]"
34
+ ```
35
+
36
+ ## Test and lint
37
+
38
+ ```bash
39
+ python3 -m pytest tests/ -v
40
+ ruff check src/ tests/
41
+ ```
42
+
43
+ ## Auth for live testing
44
+
45
+ Config at `~/.config/typefully/config.toml`. Test account: BungeeCEO.
46
+
47
+ ```bash
48
+ typefully config set api_key tf_...
49
+ typefully config set default_account BungeeCEO
50
+ ```
51
+
52
+ ## Conventions
53
+
54
+ - Python 3.9+ compatibility (no walrus operator, no `match` statements)
55
+ - Line length: 100 chars (ruff)
56
+ - All new CLI commands get the `@shared_options` decorator and `@error_handler`
57
+ - All new output.py functions must escape user content with `_esc()`
58
+ - Tests use Click's CliRunner with mock client/console/config
59
+ - No emojis in code or docs
@@ -0,0 +1,80 @@
1
+ # Contributing
2
+
3
+ ## Dev setup
4
+
5
+ ```bash
6
+ git clone https://github.com/nicedevelopers/typefully-cli.git
7
+ cd typefully-cli
8
+ pip install -e ".[dev]"
9
+ ```
10
+
11
+ ## Run tests
12
+
13
+ ```bash
14
+ python3 -m pytest tests/ -v
15
+ ```
16
+
17
+ ## Lint
18
+
19
+ ```bash
20
+ ruff check src/ tests/
21
+ ruff format --check src/ tests/
22
+ ```
23
+
24
+ ## Project structure
25
+
26
+ ```
27
+ src/typefully_cli/
28
+ cli.py -- Click commands and shared options
29
+ client.py -- Typefully API v2 HTTP client
30
+ auth.py -- API key resolution chain
31
+ config.py -- TOML config management
32
+ console.py -- stderr output (Rich)
33
+ output.py -- --text mode formatting (Rich, all user content escaped)
34
+ batch.py -- Batch file parser
35
+ media.py -- Media upload workflow
36
+ exceptions.py -- Exception hierarchy
37
+ tests/
38
+ test_cli.py -- CLI command tests (CliRunner)
39
+ test_client.py -- Client and account resolution tests
40
+ test_batch.py -- Batch parser tests
41
+ test_media.py -- Filename sanitization tests
42
+ test_output.py -- Rich markup escaping tests
43
+ test_auth.py -- Auth chain and redaction tests
44
+ ```
45
+
46
+ ## Conventions
47
+
48
+ - Python 3.9+ (no walrus, no `match`)
49
+ - Line length: 100 chars
50
+ - All CLI commands use `@shared_options` and `@error_handler`
51
+ - All output.py functions escape user content with `_esc()`
52
+ - Tests mock the Typefully API (no live calls in CI)
53
+ - JSON output is the default; `--text` is opt-in
54
+
55
+ ## PR process
56
+
57
+ 1. Fork and create a branch
58
+ 2. Make changes, add tests
59
+ 3. `python3 -m pytest tests/ -v` -- all tests pass
60
+ 4. `ruff check src/ tests/` -- no lint errors
61
+ 5. Open a PR with a clear description
62
+
63
+ ## Output contract
64
+
65
+ Any new command must follow the output contract:
66
+
67
+ - Success: `{"ok": true, "data": ...}` on stdout (exit 0)
68
+ - Error: `{"ok": false, "error": {"code": "...", "message": "...", "hint": "..."}}` on stderr (exit 1 or 2)
69
+ - Partial failure: success data on stdout + error envelope on stderr (exit 3)
70
+
71
+ ## Auth for testing
72
+
73
+ Get an API key from [Typefully Settings > API](https://typefully.com/settings/api). Set it locally:
74
+
75
+ ```bash
76
+ typefully config set api_key tf_your_key
77
+ typefully config set default_account YourAccount
78
+ ```
79
+
80
+ Live API tests are not run in CI. For local validation, use a real account.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dheeraj Shah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,263 @@
1
+ Metadata-Version: 2.4
2
+ Name: typefully-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for the Typefully API -- manage drafts, threads, and publishing from the terminal
5
+ Project-URL: Homepage, https://github.com/nicedevelopers/typefully-cli
6
+ Project-URL: Repository, https://github.com/nicedevelopers/typefully-cli
7
+ Project-URL: Issues, https://github.com/nicedevelopers/typefully-cli/issues
8
+ Project-URL: Changelog, https://github.com/nicedevelopers/typefully-cli/blob/main/CHANGELOG.md
9
+ Author: Dheeraj Shah
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,social-media,twitter,typefully,x
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Utilities
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: click>=8.0
27
+ Requires-Dist: httpx>=0.24
28
+ Requires-Dist: rich>=13.0
29
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: ruff; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # typefully-cli
37
+
38
+ Manage [Typefully](https://typefully.com) drafts, threads, and publishing from your terminal. Built for AI agents. Works great for humans too.
39
+
40
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://github.com/dheeraj-shah/typefully-cli)
41
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
42
+
43
+ ## Why
44
+
45
+ - **Automate publishing** -- schedule and publish from CI/CD pipelines, cron jobs, or scripts
46
+ - **Batch create** -- turn a text file into 50 drafts with one command
47
+ - **Agent-native** -- JSON output, structured errors, deterministic exit codes. Designed for Claude Code, LLM agents, and automation
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ pipx install typefully-cli # recommended
53
+ uv tool install typefully-cli # alternative
54
+ pip install typefully-cli # fallback
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ```bash
60
+ # First-time setup (interactive)
61
+ typefully config init
62
+
63
+ # Or set your API key directly (get it from typefully.com/settings/api)
64
+ typefully config set api_key tf_your_key_here
65
+
66
+ # Check auth
67
+ typefully me
68
+
69
+ # Create a draft
70
+ typefully draft "Hello world" --schedule next
71
+
72
+ # Create a thread
73
+ typefully thread "First post" "Second post" --tag launch
74
+
75
+ # Batch create from file
76
+ typefully batch posts.txt --schedule next --tag campaign
77
+
78
+ # Upload media and attach it
79
+ typefully upload photo.jpg
80
+ typefully draft "Check this out" --media <media_id>
81
+ ```
82
+
83
+ ## For Humans
84
+
85
+ Add `--text` to any command for readable output instead of JSON.
86
+
87
+ ```bash
88
+ typefully drafts --text
89
+ typefully recent --text --since 2025-01-01
90
+ typefully get 12345 --text
91
+ ```
92
+
93
+ **Other handy features:**
94
+
95
+ - `typefully open 12345` -- open a draft in your browser
96
+ - `typefully recent --format csv` -- export published posts to CSV
97
+ - `typefully config init` -- guided first-time setup
98
+
99
+ **Shell completions** -- add to your shell rc file:
100
+
101
+ ```bash
102
+ # Bash
103
+ eval "$(_TYPEFULLY_COMPLETE=bash_source typefully)"
104
+
105
+ # Zsh
106
+ eval "$(_TYPEFULLY_COMPLETE=zsh_source typefully)"
107
+
108
+ # Fish
109
+ _TYPEFULLY_COMPLETE=fish_source typefully | source
110
+ ```
111
+
112
+ ## For Agents
113
+
114
+ Every command writes structured JSON. Success to stdout, errors to stderr. Parse with `jq`, pipe to other tools, or consume directly from an LLM agent.
115
+
116
+ ### Output contract
117
+
118
+ ```bash
119
+ # Success (stdout)
120
+ {"ok": true, "data": ...}
121
+
122
+ # Error (stderr)
123
+ {"ok": false, "error": {"code": "auth_failed", "message": "...", "hint": "..."}}
124
+ ```
125
+
126
+ ### Exit codes
127
+
128
+ | Code | Meaning | When |
129
+ |------|---------|------|
130
+ | `0` | Success | Everything worked |
131
+ | `1` | Input/auth error | Bad credentials, missing account, invalid input |
132
+ | `2` | API error | Upstream Typefully API failure |
133
+ | `3` | Partial failure | Some items in a batch/delete succeeded, others failed |
134
+
135
+ ### Error codes
136
+
137
+ | Code | Trigger |
138
+ |------|---------|
139
+ | `auth_failed` | No API key found in flag, env, config, or 1Password |
140
+ | `no_account` | No account specified or account not found |
141
+ | `api_error` | Non-2xx response from Typefully API |
142
+ | `invalid_input` | Bad date format, limit out of range, single-post thread |
143
+ | `batch_parse_error` | Malformed batch file |
144
+ | `media_upload_error` | Upload failed or unsupported format |
145
+
146
+ ### Partial failure (batch/delete)
147
+
148
+ When some operations succeed and some fail (exit 3):
149
+ - **stdout**: success data with `deleted`/`created` array + `errors` array
150
+ - **stderr**: `partial_failure` error envelope
151
+
152
+ When all fail (exit 2): nothing on stdout, `all_failed` on stderr.
153
+
154
+ ### Common patterns
155
+
156
+ ```bash
157
+ # Suppress status messages, keep errors
158
+ typefully accounts --quiet 2>/dev/null
159
+
160
+ # Extract draft IDs
161
+ typefully drafts | jq '.data.results[].id'
162
+
163
+ # Get error hint on failure
164
+ typefully me 2>/tmp/err.json || jq '.error.hint' /tmp/err.json
165
+
166
+ # Batch create and log results
167
+ typefully batch posts.txt --output results.json
168
+ ```
169
+
170
+ ### Flags for agents
171
+
172
+ - `--quiet` -- suppress stderr status messages (errors still shown)
173
+ - `--api-key KEY` -- pass API key directly (skips config/env lookup)
174
+ - `--account NAME` -- target a specific account (accepts name, username, or numeric ID)
175
+
176
+ ## Auth
177
+
178
+ Resolution order: `--api-key` flag > `TYPEFULLY_API_KEY` env var > config file > 1Password.
179
+
180
+ Get your API key from [Typefully Settings > API](https://typefully.com/settings/api).
181
+
182
+ ```bash
183
+ # Interactive setup
184
+ typefully config init
185
+
186
+ # Or manually
187
+ typefully config set api_key tf_your_key_here
188
+ typefully config set default_account MyBrand
189
+
190
+ # Optional: 1Password fallback (requires op CLI)
191
+ typefully config set onepassword_item "Typefully API"
192
+ ```
193
+
194
+ ## Commands
195
+
196
+ All account-scoped commands accept `--api-key`, `--account`, `--text`, `--quiet`.
197
+
198
+ | Command | Description |
199
+ |---------|-------------|
200
+ | `me` | Current user info |
201
+ | `accounts` | List connected accounts with IDs |
202
+ | `account-detail [NAME]` | Platform details for an account |
203
+ | `draft "text"` | Create a single-post draft |
204
+ | `thread "p1" "p2" ...` | Create a multi-post thread (min 2 posts) |
205
+ | `get ID` | View a draft |
206
+ | `open ID` | Open a draft in the browser |
207
+ | `update ID ["text"]` | Edit a draft (use `===` to separate thread posts) |
208
+ | `delete ID [ID ...]` | Delete one or more drafts |
209
+ | `drafts` | List drafts (`--status`, `--tag`, `--limit 1-50`, `--offset`, `--order`) |
210
+ | `recent` | Published posts (`-n 1-50`, `--since`, `--until`, `--format csv`) |
211
+ | `upload FILE` | Upload media, returns media ID |
212
+ | `tags` | List tags (`--limit 1-50`, `--offset`) |
213
+ | `tag-create "name"` | Create a tag (usually auto-created via `--tag`) |
214
+ | `batch FILE` | Batch create from text file (`--dry-run`, `--output`, `--schedule`, `--tag`) |
215
+ | `config set KEY VALUE` | Set config (api_key, default_account, onepassword_item) |
216
+ | `config init` | Interactive first-time setup |
217
+ | `config show` | Show config (secrets redacted) |
218
+ | `config path` | Print config file path |
219
+
220
+ **Draft options:** `--schedule ISO|next|now`, `--tag SLUG` (repeatable, auto-created), `--title`, `--media ID`, `--share`, `--reply-to URL`, `--scratchpad`, `--qrt URL`, `--threadify`, `--auto-retweet/--no-auto-retweet`, `--auto-plug/--no-auto-plug`, `--linkedin`, `--threads`, `--bluesky`, `--mastodon`
221
+
222
+ Run `typefully COMMAND --help` for full option details.
223
+
224
+ ## Batch File Format
225
+
226
+ Drafts separated by `---`. Thread posts separated by `===`. Optional metadata headers per block.
227
+
228
+ ```
229
+ tag: launch
230
+ schedule: next
231
+ First post of a thread
232
+ ===
233
+ Second post
234
+ ---
235
+ A standalone draft
236
+ ---
237
+ tag: announcement
238
+ schedule: 2026-04-01T10:00:00Z
239
+ Another standalone draft
240
+ ```
241
+
242
+ Supported metadata: `tag`, `schedule`, `title`, `scratchpad`, `media` (comma-separated IDs). See [docs/batch-format.md](docs/batch-format.md) for the full spec.
243
+
244
+ ## Configuration
245
+
246
+ Config file: `~/.config/typefully/config.toml`
247
+
248
+ ```toml
249
+ [auth]
250
+ api_key = "tf_..."
251
+ # onepassword_item = "Typefully API"
252
+
253
+ [defaults]
254
+ account = "MyBrand"
255
+ ```
256
+
257
+ ## Contributing
258
+
259
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
260
+
261
+ ## License
262
+
263
+ MIT