typefully-cli 0.2.1__tar.gz → 0.3.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 (47) hide show
  1. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/workflows/ci.yml +3 -3
  2. typefully_cli-0.3.0/.github/workflows/monitor.yml +72 -0
  3. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/workflows/publish.yml +1 -1
  4. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CHANGELOG.md +11 -0
  5. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CLAUDE.md +10 -12
  6. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/PKG-INFO +41 -3
  7. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/README.md +40 -2
  8. typefully_cli-0.3.0/docs/quote-parity.md +79 -0
  9. typefully_cli-0.3.0/docs/upstream-parity.md +133 -0
  10. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/pyproject.toml +1 -1
  11. typefully_cli-0.3.0/scripts/monitor.py +147 -0
  12. typefully_cli-0.3.0/skills/typefully-cli/SKILL.md +76 -0
  13. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/__init__.py +1 -1
  14. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/cli.py +301 -44
  15. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/client.py +25 -4
  16. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/media.py +2 -1
  17. typefully_cli-0.3.0/src/typefully_cli/payloads.py +195 -0
  18. typefully_cli-0.3.0/src/typefully_cli/request-schemas.json +1220 -0
  19. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_cli.py +338 -0
  20. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_completions.py +2 -1
  21. typefully_cli-0.3.0/tests/test_monitor.py +69 -0
  22. typefully_cli-0.3.0/tests/test_payloads.py +216 -0
  23. typefully_cli-0.2.1/.github/workflows/monitor.yml +0 -141
  24. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/dependabot.yml +0 -0
  25. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.gitignore +0 -0
  26. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CONTRIBUTING.md +0 -0
  27. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/Formula/README.md +0 -0
  28. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/Formula/typefully-cli.rb +0 -0
  29. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/LICENSE +0 -0
  30. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/TODOS.md +0 -0
  31. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/docs/batch-format.md +0 -0
  32. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/requirements.lock +0 -0
  33. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/__main__.py +0 -0
  34. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/auth.py +0 -0
  35. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/batch.py +0 -0
  36. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/completions.py +0 -0
  37. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/config.py +0 -0
  38. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/console.py +0 -0
  39. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/exceptions.py +0 -0
  40. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/output.py +0 -0
  41. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/__init__.py +0 -0
  42. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/conftest.py +0 -0
  43. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_auth.py +0 -0
  44. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_batch.py +0 -0
  45. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_client.py +0 -0
  46. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_media.py +0 -0
  47. {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_output.py +0 -0
@@ -8,13 +8,13 @@ on:
8
8
 
9
9
  jobs:
10
10
  test:
11
- runs-on: ubuntu-latest
11
+ runs-on: ubuntu-22.04 # Python 3.9 remains supported; avoid ubuntu-latest image drift.
12
12
  strategy:
13
13
  matrix:
14
14
  python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
17
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
18
18
 
19
19
  - name: Set up Python ${{ matrix.python-version }}
20
20
  uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -25,7 +25,7 @@ jobs:
25
25
  run: pip install -e ".[dev]"
26
26
 
27
27
  - name: Lint
28
- run: ruff check src/ tests/
28
+ run: ruff check --isolated --select E4,E7,E9,F --target-version py39 --line-length 100 src/ tests/ scripts/
29
29
 
30
30
  - name: Test
31
31
  run: python -m pytest tests/ -v
@@ -0,0 +1,72 @@
1
+ name: Monitor Typefully API & Competitors
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 8 * * 1,4" # Mon & Thu 8am UTC (1:30pm IST)
6
+ workflow_dispatch: {}
7
+
8
+ concurrency:
9
+ group: typefully-upstream-monitor
10
+ cancel-in-progress: false
11
+
12
+ permissions:
13
+ issues: write
14
+ contents: read
15
+
16
+ jobs:
17
+ monitor:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21
+
22
+ - name: Restore cache
23
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
24
+ with:
25
+ path: .monitor-cache
26
+ key: monitor-cache-${{ github.run_id }}
27
+ restore-keys: monitor-cache-
28
+
29
+ - name: Fetch and validate public upstream sources
30
+ run: python3 scripts/monitor.py
31
+
32
+ - name: Report changes or degraded monitoring
33
+ env:
34
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35
+ run: |
36
+ python3 - <<'PYTHON'
37
+ import json
38
+ import subprocess
39
+ from pathlib import Path
40
+
41
+ cache = Path(".monitor-cache")
42
+ result = json.loads((cache / "result.json").read_text())
43
+ if result["report_needed"]:
44
+ # A stable title deduplicates the same unresolved change/failure.
45
+ issues = json.loads(subprocess.check_output([
46
+ "gh", "issue", "list", "--state", "open", "--search",
47
+ result["fingerprint"] + " in:title",
48
+ "--json", "title", "--limit", "100",
49
+ ], text=True))
50
+ if not any(issue["title"] == result["title"] for issue in issues):
51
+ subprocess.run([
52
+ "gh", "issue", "create", "--title", result["title"],
53
+ "--body-file", str(cache / "report.md"),
54
+ ], check=True)
55
+ subprocess.run(["python3", "scripts/monitor.py", "--commit"], check=True)
56
+ PYTHON
57
+
58
+ - name: Save cache
59
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
60
+ with:
61
+ path: .monitor-cache
62
+ key: monitor-cache-${{ github.run_id }}
63
+
64
+ - name: Fail visibly on degraded monitoring
65
+ run: |
66
+ python3 - <<'PYTHON'
67
+ import json
68
+ from pathlib import Path
69
+ result = json.loads(Path(".monitor-cache/result.json").read_text())
70
+ if result["failed"]:
71
+ raise SystemExit("Upstream fetch failed; see monitoring issue and result.json")
72
+ PYTHON
@@ -11,7 +11,7 @@ jobs:
11
11
  id-token: write # trusted publishing
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
14
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
15
15
 
16
16
  - name: Set up Python
17
17
  uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-10-29)
4
+
5
+ - Add schema-backed `draft-payload` request-file input with offline validation, per-post fields and null/false clearing; keep `--json` as output-only.
6
+ - Add X reply/subscriber/community flags, LinkedIn first-comment/reshare fields, upload alt text and media metadata updates.
7
+ - Follow social-set pagination, accept repeated tag filters and reject negative offsets; document every public API operation and unverified MCP/product-only boundaries.
8
+
9
+
10
+ - Repair upstream monitoring: validate official public sources, expose fetch failures, deduplicate reports, and advance baselines only after successful reporting.
11
+ - Pin CI to Ubuntu 22.04 to retain Python 3.9 compatibility; isolate Fish completion tests from the real home directory.
12
+ - Extend `--quote-post-url` / `--qrt` to the documented Threads, Bluesky, Mastodon and Substack quote targets.
13
+
3
14
  ## 0.2.1 (2026-08-21)
4
15
 
5
16
  - Fix trusted PyPI publishing workflow action pin.
@@ -2,13 +2,15 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Python CLI wrapping the Typefully API v2. Agent-first design: JSON output by default, structured errors on stderr, deterministic behavior. 24 Click commands for drafts, threads, batch, media, analytics, queue, LinkedIn, tags, accounts, config, completions.
5
+ Python CLI wrapping the Typefully API v2. Agent-friendly structured JSON output with `--json`, structured errors on stderr, and configurable text output. See `skills/typefully-cli/SKILL.md` for current agent usage and `docs/upstream-parity.md` for evidence and limitations.
6
6
 
7
7
  ## Architecture
8
8
 
9
9
  ```
10
10
  src/typefully_cli/
11
- cli.py -- 24 Click commands, shared options, error handler, payload builder
11
+ cli.py -- Click commands, shared options, error handler, payload builder
12
+ payloads.py -- Explicit JSON request input, local schema validation, media metadata
13
+ request-schemas.json -- Reviewed public create/update schema snapshot
12
14
  client.py -- httpx wrapper over Typefully API v2 (rate-limited, account resolution)
13
15
  completions.py -- Shell completions install/show (bash/zsh/fish)
14
16
  auth.py -- API key resolution chain: flag > env > config > 1Password
@@ -29,7 +31,7 @@ src/typefully_cli/
29
31
  - **Timezone conversion:** Config `timezone` (IANA name) converts naive schedule datetimes to UTC before API call. Already-tz-aware values pass through unchanged.
30
32
  - **Partial failure:** Delete and batch loops catch per-item errors. `_handle_multi_result()` handles the 3-branch contract (all succeed / partial / all fail).
31
33
  - **Error sanitization:** `APIError.user_message` property strips raw upstream bodies from stdout payloads. Full details stay on stderr.
32
- - **API parity:** Tracks all Typefully API v2 endpoints including analytics, queue management, and LinkedIn org resolution.
34
+ - **API parity:** Map each public operation in docs/upstream-parity.md. Do not claim exhaustive MCP parity; analytics and legacy convenience fields are not verified by the current public schema.
33
35
 
34
36
  ## Dev setup
35
37
 
@@ -41,19 +43,15 @@ pip install -e ".[dev]"
41
43
 
42
44
  ```bash
43
45
  python3 -m pytest tests/ -v
44
- ruff check src/ tests/
46
+ ruff check --isolated --select E4,E7,E9,F --target-version py39 --line-length 100 src/ tests/ scripts/
45
47
  ```
46
48
 
47
49
  ## Auth for live testing
48
50
 
49
- Config at `~/.config/typefully/config.toml`. Test account: BungeeCEO.
50
-
51
- ```bash
52
- typefully config set api_key tf_...
53
- typefully config set default_account BungeeCEO
54
- typefully config set default_platforms x,linkedin
55
- typefully config set timezone Asia/Kolkata
56
- ```
51
+ Use existing authorized access from environment/config; never assume an account
52
+ named in documentation is a sandbox. No cloud credential is bundled with this repo.
53
+ Use local `--validate-only` and mocked tests first. Live test writes need explicit
54
+ scope and a verified social set; never publish as part of maintenance tests.
57
55
 
58
56
  ## Conventions
59
57
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: typefully-cli
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: CLI for the Typefully API -- manage drafts, threads, and publishing from the terminal
5
5
  Project-URL: Homepage, https://github.com/dheeraj-shah/typefully-cli
6
6
  Project-URL: Repository, https://github.com/dheeraj-shah/typefully-cli
@@ -37,8 +37,8 @@ Description-Content-Type: text/markdown
37
37
 
38
38
  Publish to X, LinkedIn, Bluesky, Threads, Mastodon, Substack Notes, and X Articles from your terminal. Goes from idea to post without opening a browser.
39
39
 
40
- [![PyPI](https://img.shields.io/pypi/v/typefully-cli?v=1)](https://pypi.org/project/typefully-cli/)
41
- [![Python 3.9+](https://img.shields.io/pypi/pyversions/typefully-cli?v=1)](https://pypi.org/project/typefully-cli/)
40
+ [![PyPI](https://img.shields.io/pypi/v/typefully-cli.svg)](https://pypi.org/project/typefully-cli/)
41
+ [![Python 3.9+](https://img.shields.io/pypi/pyversions/typefully-cli.svg)](https://pypi.org/project/typefully-cli/)
42
42
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
43
43
 
44
44
  ## Why
@@ -338,3 +338,41 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
338
338
  ## License
339
339
 
340
340
  MIT
341
+
342
+ ## Upstream compatibility maintenance
343
+
344
+ The public-source monitor opens issues for upstream changes or failed checks; it does not
345
+ implement or merge updates. See the [source and capability baseline](docs/upstream-parity.md)
346
+ for monitored sources, known limits, and how maintenance coordinates with Dependabot.
347
+
348
+ Native quotes route to the matching selected platform (the existing `--qrt` alias still works):
349
+
350
+ ```sh
351
+ typefully draft "My take" --platform bluesky --quote-post-url https://bsky.app/profile/example.com/post/abc
352
+ ```
353
+
354
+ Threads, Bluesky, Mastodon and Substack quotes apply to the first new post. Existing X
355
+ thread behavior is retained. See [quote compatibility notes](docs/quote-parity.md).
356
+
357
+ ### Complete request input and media alt text
358
+
359
+ `draft-payload create --file draft.json --validate-only` validates the reviewed public
360
+ API schema offline. Remove `--validate-only` and select `--account` to submit.
361
+ Use `draft-payload update --draft-id ID --file update.json` for per-post settings,
362
+ explicit null/false clearing, or advanced fields. This is **request input**;
363
+ `--json` on existing commands only controls output. The new advanced commands
364
+ always emit JSON. Platform arrays replace content: retain posts/comment markers
365
+ and settings you want to keep. `publish_at: "now"` really requests publication.
366
+
367
+ `upload image.png --alt-text "Description"` sets alt text before attachment.
368
+ `media-update MEDIA_ID --alt-text "New description"` or `--clear-alt-text` changes
369
+ stored media; previously attached drafts keep their existing metadata snapshot.
370
+ See [the full public capability matrix and remaining MCP boundaries](docs/upstream-parity.md).
371
+
372
+ ### Agent skill
373
+
374
+ The repository's [Typefully CLI skill](skills/typefully-cli/SKILL.md) describes these
375
+ Python CLI commands, local validation and authorization boundaries. It is distinct
376
+ from Typefully's official JavaScript agent skill. Check the installed CLI's help:
377
+ maintenance-branch features are not available in an older published package until
378
+ an approved release is made.
@@ -2,8 +2,8 @@
2
2
 
3
3
  Publish to X, LinkedIn, Bluesky, Threads, Mastodon, Substack Notes, and X Articles from your terminal. Goes from idea to post without opening a browser.
4
4
 
5
- [![PyPI](https://img.shields.io/pypi/v/typefully-cli?v=1)](https://pypi.org/project/typefully-cli/)
6
- [![Python 3.9+](https://img.shields.io/pypi/pyversions/typefully-cli?v=1)](https://pypi.org/project/typefully-cli/)
5
+ [![PyPI](https://img.shields.io/pypi/v/typefully-cli.svg)](https://pypi.org/project/typefully-cli/)
6
+ [![Python 3.9+](https://img.shields.io/pypi/pyversions/typefully-cli.svg)](https://pypi.org/project/typefully-cli/)
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
8
 
9
9
  ## Why
@@ -303,3 +303,41 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
303
303
  ## License
304
304
 
305
305
  MIT
306
+
307
+ ## Upstream compatibility maintenance
308
+
309
+ The public-source monitor opens issues for upstream changes or failed checks; it does not
310
+ implement or merge updates. See the [source and capability baseline](docs/upstream-parity.md)
311
+ for monitored sources, known limits, and how maintenance coordinates with Dependabot.
312
+
313
+ Native quotes route to the matching selected platform (the existing `--qrt` alias still works):
314
+
315
+ ```sh
316
+ typefully draft "My take" --platform bluesky --quote-post-url https://bsky.app/profile/example.com/post/abc
317
+ ```
318
+
319
+ Threads, Bluesky, Mastodon and Substack quotes apply to the first new post. Existing X
320
+ thread behavior is retained. See [quote compatibility notes](docs/quote-parity.md).
321
+
322
+ ### Complete request input and media alt text
323
+
324
+ `draft-payload create --file draft.json --validate-only` validates the reviewed public
325
+ API schema offline. Remove `--validate-only` and select `--account` to submit.
326
+ Use `draft-payload update --draft-id ID --file update.json` for per-post settings,
327
+ explicit null/false clearing, or advanced fields. This is **request input**;
328
+ `--json` on existing commands only controls output. The new advanced commands
329
+ always emit JSON. Platform arrays replace content: retain posts/comment markers
330
+ and settings you want to keep. `publish_at: "now"` really requests publication.
331
+
332
+ `upload image.png --alt-text "Description"` sets alt text before attachment.
333
+ `media-update MEDIA_ID --alt-text "New description"` or `--clear-alt-text` changes
334
+ stored media; previously attached drafts keep their existing metadata snapshot.
335
+ See [the full public capability matrix and remaining MCP boundaries](docs/upstream-parity.md).
336
+
337
+ ### Agent skill
338
+
339
+ The repository's [Typefully CLI skill](skills/typefully-cli/SKILL.md) describes these
340
+ Python CLI commands, local validation and authorization boundaries. It is distinct
341
+ from Typefully's official JavaScript agent skill. Check the installed CLI's help:
342
+ maintenance-branch features are not available in an older published package until
343
+ an approved release is made.
@@ -0,0 +1,79 @@
1
+ # Quote-post API parity (verified 2026-10-29)
2
+
3
+ `draft`, `thread`, and `update` accept `--quote-post-url` (existing alias `--qrt`)
4
+ for X, Threads, Bluesky, Mastodon, and Substack Notes. The URL chooses which
5
+ selected platform receives the quote. LinkedIn reposts are a separate feature.
6
+
7
+ ```sh
8
+ typefully draft "Worth reading" --platform bluesky,linkedin \
9
+ --quote-post-url https://bsky.app/profile/example.bsky.social/post/abc
10
+ typefully update DRAFT_ID --platform mastodon \
11
+ --quote-post-url https://mastodon.social/@example/123
12
+ ```
13
+
14
+ Only Bluesky receives the quote in the first example. The second keeps existing
15
+ text. Replacing text replaces posts, so provide the quote flag again to retain a
16
+ quote. Append preserves existing posts and attaches the quote to new content.
17
+ Substack supports one post and cannot append to an existing note.
18
+
19
+ Known hosts route to their own platform; custom publication paths `/note/c-...`
20
+ and `/p/...` route to Substack when selected. Other hosts prefer Mastodon when
21
+ selected, otherwise the sole quote-capable target. Ambiguous/mismatched targets
22
+ fail locally. The API still validates whether a particular post can be quoted.
23
+
24
+ For compatibility, X quotes continue to apply to every supplied thread post.
25
+ New quote platforms apply the quote to the first supplied post only. Metadata-only
26
+ quote changes do not rewrite unrelated platforms. No real posts are created by
27
+ the tests.
28
+
29
+ ## Evidence and boundaries
30
+
31
+ - [API reference](https://typefully.com/docs/api): request schemas for XPost,
32
+ ThreadsPost, BlueskyPost, MastodonPost and SubstackPost expose `quote_post_url`.
33
+ - [Official quote guide](https://github.com/typefully/agent-skills/blob/main/skills/typefully/references/quotes.md)
34
+ - [Official routing implementation](https://github.com/typefully/agent-skills/blob/main/skills/typefully/scripts/typefully.js):
35
+ `classifyQuoteUrl` and `resolveQuotePlatform`.
36
+ - [Product changelog](https://typefully.com/changelog): October 9 expanded quotes.
37
+
38
+ ## X and LinkedIn platform fields
39
+
40
+ The following additive options are available on `draft`, `thread`, and `update`:
41
+
42
+ | Option | API field and behavior |
43
+ | --- | --- |
44
+ | `--reply-settings following\|mentionedUsers\|subscribers\|verified\|null` | X post `reply_settings`; `null` restores the default reply audience |
45
+ | `--subscribers-only` / `--no-subscribers-only` | X post `subscribers_only`; omission does not change the field |
46
+ | `--share-with-followers` / `--no-share-with-followers` | X `settings.share_with_followers`; requires `--community` or an existing community on an update |
47
+ | `--linkedin-first-comment TEXT` | LinkedIn `settings.first_comment`, at most 1250 characters; `null` or an empty string clears it |
48
+ | `--linkedin-reshare-target URL_OR_URN` | First supplied LinkedIn post `linkedin_reshare_target`, at most 2048 characters; `null` clears it |
49
+
50
+ LinkedIn reshares accept a full post URL or canonical `urn:li:share:...`,
51
+ `urn:li:ugcPost:...`, or `urn:li:groupPost:...`. Upstream validates and normalizes
52
+ the target; the CLI enforces the documented length limit. Responses expose
53
+ `linkedin_reshare_urn`, which the CLI translates back to the writable
54
+ `linkedin_reshare_target` when preserving existing content.
55
+
56
+ ```sh
57
+ typefully draft "Community update" --platform x --community 123 \
58
+ --no-share-with-followers --reply-settings following
59
+ typefully update DRAFT_ID --platform linkedin --linkedin-first-comment "More details here"
60
+ typefully update DRAFT_ID --platform x --reply-settings null --no-subscribers-only
61
+ ```
62
+
63
+ X post options apply to all supplied posts; LinkedIn reposts apply to the first
64
+ supplied post. Append applies post options only to newly appended posts and
65
+ merges settings without losing existing values. Metadata-only updates retain
66
+ text (including comment anchors), media, quotes, restrictions, and settings;
67
+ unrelated platforms are not rewritten. Text replacement continues to replace
68
+ posts wholesale. Wrong-platform options, oversized values, and community share
69
+ options without a community fail locally before the draft update request.
70
+ Media plus platform options is handled in the same content-preserving update
71
+ path. A media-only update without `--platform` retains the historical X default.
72
+
73
+ These fields are verified against `XPost`, `XSettings`, `LinkedInPost`, and
74
+ `LinkedInSettings` in the public [OpenAPI reference](https://typefully.com/docs/api),
75
+ not inferred from MCP or product UI capabilities.
76
+
77
+ Polls were announced in the product changelog, but no poll request schema was
78
+ found in the current API reference, so no API support is inferred. The MCP OAuth
79
+ connector setup is transport/auth functionality, not an API-key CLI parity gap.
@@ -0,0 +1,133 @@
1
+ # Typefully upstream parity baseline
2
+
3
+ Reviewed 2026-10-29 against CLI 0.2.1 / main `82fd5948`.
4
+
5
+ ## Sources and limits
6
+
7
+ - [Public API reference](https://typefully.com/docs/api): endpoint and payload authority.
8
+ - [Official agent skills](https://github.com/typefully/agent-skills/tree/main/skills/typefully): usage and platform references. The executable reference is `skills/typefully/scripts/typefully.js`, not `/scripts/typefully.js`.
9
+ - [Official MCP guide](https://support.typefully.com/en/articles/13128440-typefully-mcp-server): public capability overview. The MCP endpoint requires authentication and the MCP initialization protocol; unauthenticated `tools/list` is not a valid inventory.
10
+ - [Product changelog](https://typefully.com/changelog): discovery of changes, not evidence that every product feature is exposed through the public API.
11
+
12
+ This is not a claim of exhaustive behavioral parity. An authenticated MCP inventory was subsequently verified below. Do not guess endpoints from product announcements. The previous CLI `--json` flag controlled output only; it was not request-body passthrough. This maintenance adds an explicit `draft-payload` command, described below.
13
+
14
+ ## Monitor repair
15
+
16
+ The previous workflow treated the API root greeting as an OpenAPI schema, used a missing agent-skills path, attempted unauthenticated MCP enumeration, and swallowed fetch failures. It could therefore report no changes despite incomplete coverage.
17
+
18
+ The replacement monitors validated public API documentation, official agent source, public MCP documentation, and the changelog. It ignores scripts/styles in HTML to reduce dynamic-build noise. It does **not** label documentation as an OpenAPI schema, enumerate private MCP tools, require a new credential, or claim a successful fetch proves semantic feature parity. The old npm probe is removed: the official skill source is the maintained reference, and a package version was not established as an authoritative API contract.
19
+
20
+ First valid reads establish a baseline. Changes or failures create a deduplicated issue. Failures preserve the last good source baseline and mark the workflow failed. Baselines advance only after reporting succeeds, so an issue-delivery failure does not silently lose a change. The existing Monday/Thursday cron and `issues: write` / `contents: read` permissions are unchanged; this remains issue-only automation.
21
+
22
+ Run public-source validation without creating an issue:
23
+
24
+ ```sh
25
+ python3 scripts/monitor.py --cache-dir /tmp/typefully-monitor-check
26
+ ```
27
+
28
+ This performs public GET requests only. Tests use injected fake responses and never call Typefully accounts.
29
+
30
+ ## Maintenance coordination
31
+
32
+ Dependabot's existing five open Actions-upgrade PRs (#9, #11, #12, #13, #14 at review time) are not duplicated here. CI is pinned to Ubuntu 22.04 to keep the advertised Python 3.9–3.13 matrix working rather than silently dropping Python 3.9 after `ubuntu-latest` moved to Ubuntu 26.04.
33
+
34
+ Publishing remains unchanged and is triggered by `v*` tags. Maintenance branches and draft PRs must not create tags, publish packages, merge, or mutate real Typefully content as tests.
35
+
36
+
37
+ ## Complete public API capability matrix (2026-10-29 snapshot)
38
+
39
+ The API page embeds an OpenAPI 3.1 specification: **24 HTTP operations across 16 paths**. The following maps every operation, not just newly changed features. Paths below are relative to `/v2`; `S` denotes `/social-sets/{social_set_id}` and `D` denotes `S/drafts/{draft_id}`.
40
+
41
+ | Public API operation(s) | CLI surface | Evidence / qualification |
42
+ | --- | --- | --- |
43
+ | GET `/me` | `me` | Existing |
44
+ | GET `/social-sets` | `accounts`, account resolution | Now follows all 50-item pages instead of truncating at 50 |
45
+ | GET `S/` | `account` | Existing detail command |
46
+ | GET / POST `S/drafts` | `drafts`; `draft`, `thread`, `batch`, `draft-payload create` | Repeated `--tag` filters supported; JSON input covers per-platform/per-post fields |
47
+ | GET / PATCH / DELETE `D` | `get`; `update`, `publish`, `schedule`, `plan`, `draft-payload update`; `delete` | Explicit JSON null/false/empty arrays survive unchanged; server content/comment rules still apply |
48
+ | POST `S/media/upload` | `upload --alt-text` | Alt text up to 1000 characters, including empty text |
49
+ | GET / PATCH `S/media/{media_id}` | `media-status`; **new `media-update`** | Patch supports replacement and explicit null clearing |
50
+ | GET / POST `S/tags` | `tags`, `tag-create` | Existing |
51
+ | GET / PUT `S/queue/schedule` | `queue-schedule get/set` | Existing |
52
+ | GET `S/queue` | `queue` | Existing |
53
+ | GET `S/linkedin/organizations/resolve` | `linkedin-resolve` | Existing |
54
+ | GET / POST `D/comment-threads` | `comments list/create` | Existing; pagination bounds checked |
55
+ | POST `D/comment-threads/{thread}/comments` | `comments reply` | Existing |
56
+ | POST `D/comment-threads/{thread}/resolve` | `comments resolve` | Existing |
57
+ | PATCH / DELETE `D/comment-threads/{thread}/comments/{comment}` | `comments update/delete --comment-id` | Existing |
58
+ | DELETE `D/comment-threads/{thread}` | `comments delete` without comment ID | Existing |
59
+
60
+ This is **operation coverage**, not proof that every account-dependent behavior has been exercised against the live service. All maintenance tests are offline/mocked.
61
+
62
+ ### Request fields and platform behavior
63
+
64
+ - New dedicated create/thread/update flags: X `reply_settings` (`following`, `mentionedUsers`, `subscribers`, `verified`, or `null`), `subscribers_only` including false; community `share_with_followers` including false; LinkedIn `first_comment` (1250 characters, null/empty clears) and `linkedin_reshare_target` (URL or URN, 2048 characters, null clears).
65
+ - Response `linkedin_reshare_urn` is converted to writable `linkedin_reshare_target` when updating copied posts. Other platform posts, settings and comment-marked text are preserved for metadata-only updates.
66
+ - Quotes: X, Threads, Bluesky, Mastodon and Substack supported; LinkedIn uses its separate reshare field, not `quote_post_url`.
67
+ - The explicit request-input command covers the reviewed schema for all **seven** platform keys: X, LinkedIn, Threads, Bluesky, Mastodon, Substack, and X Article. It preserves per-post distinctions, settings, media, tags, scheduling/planning, null-clearing, false toggles, and `force_overwrite_comments` on update. It does not automatically remove comment markers or overwrite comments.
68
+ - `src/typefully_cli/request-schemas.json` is the API page's transitive create/update schema snapshot, with source and retrieval date. The dependency-free validator implements only that snapshot's finite keyword vocabulary; regression tests reject unrecognized future schema keywords. Type, required/unknown fields, conditional fields, lengths, enums and timezone-aware date-time shape are checked locally. Server-side eligibility, URL/URN validity, supported media/platform combinations, scheduling limits and account permissions remain authoritative.
69
+ - Media alt text is snapshotted when attached to a draft. Updating stored media does not silently rewrite already-attached drafts. Intentionally resend draft content to refresh an attachment after updating its media.
70
+ - Existing `--json` remains an **output** switch. New request input uses `--file`, including stdin (`-`). It is explicit, not an arbitrary HTTP escape hatch.
71
+
72
+ ```sh
73
+ # Pure local validation: no account, token or network required.
74
+ typefully draft-payload create --file draft.json --validate-only
75
+ # These commands mutate your chosen account only when explicitly invoked:
76
+ typefully draft-payload create --file draft.json --account my-account
77
+ typefully draft-payload update --draft-id 123 --file update.json --account my-account
78
+ typefully media-update MEDIA_ID --alt-text "Descriptive image text" --account my-account
79
+ typefully media-update MEDIA_ID --clear-alt-text --account my-account
80
+ ```
81
+
82
+ Update payloads containing platform post arrays replace content, rather than magically merging missing posts. Include content/settings to retain. `publish_at: "now"` is a real publication request; `plan_at` is inert planning. Local validation cannot certify that a request will succeed on every social network.
83
+
84
+ ### Remaining boundaries, not fabricated parity
85
+
86
+ 1. **Authenticated MCP inventory verified, behavior bounded.** On 2026-10-29, an explicitly authorized one-off local check using existing authentication initialized server `typefully 1.0.0` (protocol `2025-03-26`) and retrieved all 24 tools, with no continuation page. Every tool maps to the public operation matrix above; no additional MCP-only endpoint was found. Draft create/edit bodies match the reviewed public schemas apart from MCP-only `confirm_publish` (default false, marked `x-mcp-only`), which requires explicit confirmation for immediate publication. This field must not be sent to REST. No MCP tools were invoked. Inventory inspection is not an all-platform publishing test.
87
+ 2. **Polls:** product announcements are not a writable public API contract. No poll request schema was found in this snapshot, so no guessed endpoint or flags were added.
88
+ 3. **Analytics / follower analytics:** existing CLI routes are preserved, but absent from this particular 24-operation public snapshot. Their current contract is unverified here, not automatically declared supported or removed.
89
+ 4. **Webhook management:** a webhook event payload schema exists, but no public management operations in the snapshot. Do not infer create/delete subscription endpoints.
90
+ 5. **Legacy `threadify` / auto-retweet / auto-plug convenience fields:** preserved for compatibility, but not in the reviewed draft request schema. New `draft-payload` rejects undocumented fields instead of pretending they are supported.
91
+ 6. **Defaults and syntax:** the Python CLI retains its established account/default-platform resolution instead of copying the official agent script's interface. Equivalent supported capabilities do not require identical flags or defaults.
92
+ 7. **Product/media rollout differences:** product announcements (for example Substack video) can outpace platform guidance. Do not guarantee an account/platform media combination solely from a changelog item.
93
+
94
+ Weekly maintenance must re-check this entire matrix, implement verified gaps even if they predate the latest diff, update the request snapshot/validator together, and leave unsupported or inaccessible capabilities explicitly marked. A failed source read is degraded monitoring, not evidence of no change.
95
+
96
+
97
+ ### Authenticated MCP comparison
98
+
99
+ Inventory names: `get_me`, `list_social_sets`, `get_social_set_details`, `list_drafts`,
100
+ `create_draft`, `get_draft`, `delete_draft`, `edit_draft`, `create_media_upload`,
101
+ `get_media_status`, `update_media`, `list_tags`, `create_tag`, `get_queue_schedule`,
102
+ `queue_put_queue_schedule`, `get_queue`,
103
+ `linkedin_resolve_linkedin_organization_from_url`, `list_comments`, `create_comment`,
104
+ `comments_add_comment_to_thread`, `comments_resolve_thread`, `delete_comment`,
105
+ `update_comment`, `delete_thread`.
106
+
107
+ Remaining interface/validation differences are explicit rather than silently
108
+ labelled full parity: MCP exposes social-set limit/offset while CLI auto-paginates;
109
+ MCP requires queue start/end dates whereas CLI can defer missing-date errors to the
110
+ server; several comment/tag length, UUID, and queue-rule constraints are validated
111
+ server-side rather than exhaustively by existing CLI convenience commands. Analytics,
112
+ follower analytics and draft `content_filter` were absent from this MCP inventory.
113
+
114
+ The MCP read-only annotation on `get_queue_schedule` does **not** imply zero state
115
+ changes: its description says an absent schedule is initialized with defaults.
116
+ Exclude that operation from strictly non-mutating checks. MCP deletion annotations
117
+ also do not establish recoverability or replace applicable user authorization.
118
+
119
+ ### Bounded live smoke test
120
+
121
+ On 2026-10-29, the user explicitly approved a one-off test using existing local
122
+ credentials and selected the destination account. Exact CLI commit
123
+ `fc19975c755661ba48d92d9d4f11ce7ce6ae31fd` created one labelled private, unscheduled
124
+ X draft (201), read it (200), updated dummy text and `reply_settings: "following"`
125
+ (200), verified the update (200), deleted only the new draft (204), and verified
126
+ absence (404). Readbacks remained draft-only, with null scheduling/publication
127
+ fields and no public share URL. No test draft remains. No credentials or private
128
+ account receipts are included in this repository.
129
+
130
+ This proves that bounded X draft lifecycle against the live service, not live
131
+ publishing, scheduling, media upload, other platforms, or MCP tool execution.
132
+ The normal maintenance workflow remains cloud-only; the local test was separately
133
+ approved because existing authentication was available there, not in the cloud.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "typefully-cli"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "CLI for the Typefully API -- manage drafts, threads, and publishing from the terminal"
9
9
  readme = "README.md"
10
10
  license = "MIT"