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.
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/workflows/ci.yml +3 -3
- typefully_cli-0.3.0/.github/workflows/monitor.yml +72 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/workflows/publish.yml +1 -1
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CHANGELOG.md +11 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CLAUDE.md +10 -12
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/PKG-INFO +41 -3
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/README.md +40 -2
- typefully_cli-0.3.0/docs/quote-parity.md +79 -0
- typefully_cli-0.3.0/docs/upstream-parity.md +133 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/pyproject.toml +1 -1
- typefully_cli-0.3.0/scripts/monitor.py +147 -0
- typefully_cli-0.3.0/skills/typefully-cli/SKILL.md +76 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/__init__.py +1 -1
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/cli.py +301 -44
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/client.py +25 -4
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/media.py +2 -1
- typefully_cli-0.3.0/src/typefully_cli/payloads.py +195 -0
- typefully_cli-0.3.0/src/typefully_cli/request-schemas.json +1220 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_cli.py +338 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_completions.py +2 -1
- typefully_cli-0.3.0/tests/test_monitor.py +69 -0
- typefully_cli-0.3.0/tests/test_payloads.py +216 -0
- typefully_cli-0.2.1/.github/workflows/monitor.yml +0 -141
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.github/dependabot.yml +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/.gitignore +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/Formula/README.md +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/Formula/typefully-cli.rb +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/LICENSE +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/TODOS.md +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/docs/batch-format.md +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/requirements.lock +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/__main__.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/auth.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/batch.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/completions.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/config.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/console.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/exceptions.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/src/typefully_cli/output.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/__init__.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/conftest.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_auth.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_batch.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_client.py +0 -0
- {typefully_cli-0.2.1 → typefully_cli-0.3.0}/tests/test_media.py +0 -0
- {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@
|
|
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@
|
|
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-
|
|
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 --
|
|
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:**
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
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
|
-
[](https://pypi.org/project/typefully-cli/)
|
|
41
|
+
[](https://pypi.org/project/typefully-cli/)
|
|
42
42
|
[](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
|
-
[](https://pypi.org/project/typefully-cli/)
|
|
6
|
+
[](https://pypi.org/project/typefully-cli/)
|
|
7
7
|
[](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.
|