domain-pre-flight 0.9.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 (99) hide show
  1. domain_pre_flight-0.9.0/.claude/skills/domain-pre-flight/SKILL.md +196 -0
  2. domain_pre_flight-0.9.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
  3. domain_pre_flight-0.9.0/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  4. domain_pre_flight-0.9.0/.github/pull_request_template.md +22 -0
  5. domain_pre_flight-0.9.0/.github/workflows/publish.yml +40 -0
  6. domain_pre_flight-0.9.0/.github/workflows/refresh-known-brands.yml +42 -0
  7. domain_pre_flight-0.9.0/.github/workflows/refresh-tld-risk.yml +41 -0
  8. domain_pre_flight-0.9.0/.github/workflows/release.yml +41 -0
  9. domain_pre_flight-0.9.0/.github/workflows/test.yml +57 -0
  10. domain_pre_flight-0.9.0/.gitignore +22 -0
  11. domain_pre_flight-0.9.0/CHANGELOG.md +63 -0
  12. domain_pre_flight-0.9.0/CLAUDE.md +118 -0
  13. domain_pre_flight-0.9.0/CODE_OF_CONDUCT.md +23 -0
  14. domain_pre_flight-0.9.0/CONTRIBUTING.md +50 -0
  15. domain_pre_flight-0.9.0/LICENSE +21 -0
  16. domain_pre_flight-0.9.0/PKG-INFO +241 -0
  17. domain_pre_flight-0.9.0/README.md +202 -0
  18. domain_pre_flight-0.9.0/SECURITY.md +26 -0
  19. domain_pre_flight-0.9.0/docs/agents/README.md +13 -0
  20. domain_pre_flight-0.9.0/docs/agents/adding-platforms.md +119 -0
  21. domain_pre_flight-0.9.0/docs/agents/data-updates.md +130 -0
  22. domain_pre_flight-0.9.0/docs/agents/extending-checks.md +179 -0
  23. domain_pre_flight-0.9.0/docs/agents/tuning-scores.md +95 -0
  24. domain_pre_flight-0.9.0/docs/agents/writing-tests.md +91 -0
  25. domain_pre_flight-0.9.0/docs/architecture.md +156 -0
  26. domain_pre_flight-0.9.0/docs/context-cards/README.md +38 -0
  27. domain_pre_flight-0.9.0/docs/context-cards/checks-basic.md +65 -0
  28. domain_pre_flight-0.9.0/docs/context-cards/checks-handles.md +89 -0
  29. domain_pre_flight-0.9.0/docs/context-cards/checks-history.md +61 -0
  30. domain_pre_flight-0.9.0/docs/context-cards/checks-llmo.md +65 -0
  31. domain_pre_flight-0.9.0/docs/context-cards/checks-score.md +100 -0
  32. domain_pre_flight-0.9.0/docs/context-cards/checks-semantics.md +66 -0
  33. domain_pre_flight-0.9.0/docs/context-cards/checks-trademark.md +79 -0
  34. domain_pre_flight-0.9.0/docs/context-cards/checks-typosquat.md +77 -0
  35. domain_pre_flight-0.9.0/docs/context-cards/cli.md +86 -0
  36. domain_pre_flight-0.9.0/docs/decisions/0001-tldextract-for-psl-parsing.md +24 -0
  37. domain_pre_flight-0.9.0/docs/decisions/0002-band-as-str-enum-not-strenum.md +30 -0
  38. domain_pre_flight-0.9.0/docs/decisions/0003-tld-risk-as-json-bundle.md +31 -0
  39. domain_pre_flight-0.9.0/docs/decisions/0004-wayback-bounded-count.md +40 -0
  40. domain_pre_flight-0.9.0/docs/decisions/0005-bot-walled-platforms-return-unknown.md +33 -0
  41. domain_pre_flight-0.9.0/docs/decisions/0006-trademark-default-off.md +32 -0
  42. domain_pre_flight-0.9.0/docs/decisions/0007-score-weights-centralised.md +30 -0
  43. domain_pre_flight-0.9.0/docs/decisions/0008-llmo-experimental-marker.md +28 -0
  44. domain_pre_flight-0.9.0/docs/decisions/0009-trademark-deeplink-only.md +52 -0
  45. domain_pre_flight-0.9.0/docs/decisions/README.md +44 -0
  46. domain_pre_flight-0.9.0/docs/guide/usage.md +362 -0
  47. domain_pre_flight-0.9.0/examples/subprocess-variant/README.md +34 -0
  48. domain_pre_flight-0.9.0/examples/subprocess-variant/bench.py +83 -0
  49. domain_pre_flight-0.9.0/examples/subprocess-variant/server.py +99 -0
  50. domain_pre_flight-0.9.0/pyproject.toml +97 -0
  51. domain_pre_flight-0.9.0/scripts/refresh_known_brands.py +142 -0
  52. domain_pre_flight-0.9.0/scripts/refresh_tld_risk.py +112 -0
  53. domain_pre_flight-0.9.0/scripts/smoke.sh +122 -0
  54. domain_pre_flight-0.9.0/src/domain_pre_flight/__init__.py +3 -0
  55. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/__init__.py +1 -0
  56. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/basic.py +149 -0
  57. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/dns_sanity.py +157 -0
  58. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/handles.py +178 -0
  59. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/history.py +106 -0
  60. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/idn_homograph.py +110 -0
  61. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/llmo.py +189 -0
  62. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/permutations.py +162 -0
  63. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/rdap.py +174 -0
  64. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/score.py +219 -0
  65. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/semantics.py +117 -0
  66. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/trademark.py +124 -0
  67. domain_pre_flight-0.9.0/src/domain_pre_flight/checks/typosquat.py +148 -0
  68. domain_pre_flight-0.9.0/src/domain_pre_flight/cli.py +569 -0
  69. domain_pre_flight-0.9.0/src/domain_pre_flight/data/__init__.py +0 -0
  70. domain_pre_flight-0.9.0/src/domain_pre_flight/data/known_brands.txt +158 -0
  71. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/__init__.py +0 -0
  72. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/ar.txt +9 -0
  73. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/en.txt +23 -0
  74. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/es.txt +15 -0
  75. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/hi.txt +12 -0
  76. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/id.txt +11 -0
  77. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/ja.txt +16 -0
  78. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/ko.txt +9 -0
  79. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/pt.txt +12 -0
  80. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/th.txt +7 -0
  81. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/vi.txt +8 -0
  82. domain_pre_flight-0.9.0/src/domain_pre_flight/data/negative_meanings/zh.txt +11 -0
  83. domain_pre_flight-0.9.0/src/domain_pre_flight/data/tld_risk.json +45 -0
  84. domain_pre_flight-0.9.0/src/domain_pre_flight/mcp_server.py +139 -0
  85. domain_pre_flight-0.9.0/tests/__init__.py +0 -0
  86. domain_pre_flight-0.9.0/tests/test_basic.py +59 -0
  87. domain_pre_flight-0.9.0/tests/test_cli.py +281 -0
  88. domain_pre_flight-0.9.0/tests/test_dns_sanity.py +119 -0
  89. domain_pre_flight-0.9.0/tests/test_handles.py +178 -0
  90. domain_pre_flight-0.9.0/tests/test_idn_homograph.py +44 -0
  91. domain_pre_flight-0.9.0/tests/test_llmo.py +62 -0
  92. domain_pre_flight-0.9.0/tests/test_mcp_server.py +68 -0
  93. domain_pre_flight-0.9.0/tests/test_permutations.py +51 -0
  94. domain_pre_flight-0.9.0/tests/test_rdap.py +116 -0
  95. domain_pre_flight-0.9.0/tests/test_score.py +30 -0
  96. domain_pre_flight-0.9.0/tests/test_semantics.py +65 -0
  97. domain_pre_flight-0.9.0/tests/test_tld_risk_bundle.py +39 -0
  98. domain_pre_flight-0.9.0/tests/test_trademark.py +57 -0
  99. domain_pre_flight-0.9.0/tests/test_typosquat.py +64 -0
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: domain-pre-flight
3
+ description: Pre-flight checks before registering a domain for a new site or app. Runs structural / history / typosquat / multi-language / LLMO checks against one or more candidates, optionally adds same-name handle availability and trademark deeplinks, and returns per-domain verdict bands plus (when given multiple candidates) a comparison table and a top-3 trademark verify checklist. Use when the user is choosing a domain, evaluating a brainstorm shortlist, or wants a sanity check before pulling the trigger on a registrar.
4
+ argument-hint: <domain> [<domain> ...] [--full] [--handles] [--trademark]
5
+ allowed-tools: Bash, Read
6
+ ---
7
+
8
+ # domain-pre-flight skill
9
+
10
+ Run pre-flight checks before the user registers a new domain. The CLI lives at https://github.com/kenimo49/domain-pre-flight; this skill is the conversational wrapper around it.
11
+
12
+ ## When to invoke
13
+
14
+ Use this skill when the user:
15
+
16
+ - explicitly asks "is this domain safe to register" / "can I take X.com" / "ドメイン取って大丈夫?"
17
+ - gives a list of candidate domains and asks which one to pick
18
+ - mentions registering a name and you want to add a sanity check before they spend money
19
+ - references the companion book `domain-hunter-engineer-collab` or "domain hunter" workflow
20
+
21
+ Do NOT invoke when:
22
+
23
+ - the user is asking about a domain they already own (use the trademark / typosquat subcommands manually if needed, but the full pre-flight is for unregistered candidates)
24
+ - the question is about domain investing / aftermarket / drop-catching (out of scope for this tool)
25
+
26
+ ## How it works
27
+
28
+ 1. **Resolve the CLI.** Try in this order and use the first that exists:
29
+ ```bash
30
+ DPF=$(command -v dpf 2>/dev/null \
31
+ || command -v domain-pre-flight 2>/dev/null \
32
+ || ([ -x "$HOME/repos/domain-pre-flight/.venv/bin/dpf" ] && echo "$HOME/repos/domain-pre-flight/.venv/bin/dpf"))
33
+ ```
34
+ If `$DPF` is empty, see the install section below.
35
+ 2. **Detect single vs multi.** If the user gave one domain, follow the [single-domain](#single-domain) flow. If two or more, follow the [multi-domain](#multi-domain) flow.
36
+ 3. **Always pass `--json`** to the CLI and parse with `python3 -c 'import json, sys; ...'`. Never read coloured terminal output.
37
+
38
+ ## Install (only if `$DPF` did not resolve)
39
+
40
+ ```bash
41
+ # Recommended — pipx isolates the CLI in its own venv but exposes `dpf` on PATH
42
+ pipx install git+https://github.com/kenimo49/domain-pre-flight.git
43
+
44
+ # Or, from a local clone in editable mode
45
+ pip install -e /home/iris/repos/domain-pre-flight
46
+ ```
47
+
48
+ ## Single-domain
49
+
50
+ ```bash
51
+ "$DPF" check "$domain" --json # quick (default)
52
+ "$DPF" check "$domain" --check-handles --check-trademark --json # full pre-flight
53
+ ```
54
+
55
+ Runs basic + history + typosquat + semantics + llmo (`--json` always). The `--check-handles` and `--check-trademark` flags are opt-in for slower / network-heavy / manual-verify paths.
56
+
57
+ Summarise in 3–6 lines:
58
+
59
+ ```
60
+ **<domain>**: <band> — score <N>/100
61
+
62
+ What flagged:
63
+ - <deduction reason 1>: -<points>
64
+ - <deduction reason 2>: -<points>
65
+
66
+ Recommendation: <one sentence — register, walk away, or run a deeper check>
67
+ ```
68
+
69
+ Keep the report under ~6 lines unless the user asks for the full breakdown. The `verdict.deductions` list in the JSON is the source of truth — quote it directly rather than paraphrasing (the wording is calibrated).
70
+
71
+ If a check returned `lookup_failed` or `unknown` (e.g. handles bot wall), say so explicitly. Do not pretend you got a clean answer.
72
+
73
+ ## Multi-domain
74
+
75
+ Run the candidates in parallel via background bash jobs, write each to a temp JSON, then aggregate. Pattern:
76
+
77
+ ```bash
78
+ mkdir -p /tmp/dpf-batch
79
+ DOMAINS=("$@") # the user's argument list
80
+ for d in "${DOMAINS[@]}"; do
81
+ "$DPF" check "$d" --check-handles --check-trademark --json \
82
+ > "/tmp/dpf-batch/$d.json" 2> "/tmp/dpf-batch/$d.err" &
83
+ done
84
+ wait
85
+ ```
86
+
87
+ 8 candidates running in parallel typically finish inside 30–60 seconds. Read each JSON with `python3 -c 'import json, sys; ...'` and assemble the [output](#multi-domain-output).
88
+
89
+ For ad-hoc shortlists (≤16 candidates) parallel runs are fine. For larger lists chunk into batches of 8 to avoid overwhelming Wayback / public APIs.
90
+
91
+ ### Multi-domain output
92
+
93
+ Produce three sections, in this order:
94
+
95
+ #### 1. Comparison table
96
+
97
+ One row per candidate. Columns:
98
+
99
+ | domain | band | score | top deductions | github | npm | pypi | x/ig | tld notes |
100
+
101
+ - `band` uses the colour emoji: 🟢 GREEN / 🟡 YELLOW / 🟠 ORANGE / 🔴 RED.
102
+ - `top deductions` keeps the 1–2 highest-points entries from `verdict.deductions` (verbatim).
103
+ - `github` / `npm` / `pypi` show ✅ for `available`, ❌ for `taken`, ❓ for `unknown`.
104
+ - `x/ig` is collapsed because both X and Instagram are bot-walled and frequently `unknown` — render as `❓/❓` unless one of them came back `available` (404), then show the actual symbols.
105
+ - `tld notes` flags new gTLDs (`.engineer`, `.app`, etc.) that have a known SEO ramp-up disadvantage even when the band is green.
106
+
107
+ #### 2. Ranking
108
+
109
+ Pick the **top 3** candidates by:
110
+
111
+ 1. Verdict band (GREEN > YELLOW > ORANGE > RED).
112
+ 2. Within band, by `verdict.score` desc.
113
+ 3. Tie-breaker by handles availability (more ✅ wins).
114
+
115
+ For each top-3 entry, give:
116
+
117
+ - 🥇 / 🥈 / 🥉 medal + domain
118
+ - 1-line strategic rationale (relate to the user's stated project goals if known)
119
+ - 1-line weakness or risk
120
+ - handles status summary
121
+
122
+ #### 3. Top-3 trademark verify checklist
123
+
124
+ **This is the headline UX of multi-domain mode.** Render only the **top 3** candidates' trademark deeplinks, as a 3×3 markdown table:
125
+
126
+ | candidate | USPTO | EUIPO | J-PlatPat |
127
+ |-----------|-------|-------|-----------|
128
+ | **<domain1>** | [search](url) | [search](url) | [search](url) |
129
+ | <domain2> | [search](url) | [search](url) | [search](url) |
130
+ | <domain3> | [search](url) | [search](url) | [search](url) |
131
+
132
+ The deeplink URLs come straight from the CLI's `trademark.jurisdictions[].deeplink` field — do not reconstruct them by hand.
133
+
134
+ Tell the user: **trademark verification is manual** (per ADR 0009 — none of the registries publishes a stable public search API). 9 link clicks for the top 3 candidates is the minimum honest verify pass; anything beyond top 3 is over-clicking unless a top-3 entry hits a conflict and the next candidate inherits the slot.
135
+
136
+ For the **bottom candidates (rank 4+)**, do NOT render their trademark deeplinks. Only flag if the entire top 3 has issues, in which case re-run the verify checklist for ranks 4–6 explicitly when asked.
137
+
138
+ #### 4. Falsifiable next step
139
+
140
+ End with one sentence telling the user what to do next: e.g. *"Click the 9 links above; if any USPTO/EUIPO entry returns a hit in class 9 / 33 / 35, the candidate falls out and rank #4 takes its slot."* Make it concrete enough that the user knows when they are done.
141
+
142
+ ## Targeted single-axis checks
143
+
144
+ When the user only cares about one axis, call the corresponding subcommand directly:
145
+
146
+ | User intent | Run |
147
+ | --------------------------------------- | -------------------------------------- |
148
+ | "Is the GitHub / npm name free?" | `"$DPF" handles <domain> --json` |
149
+ | "Does this name look like a typosquat?" | `"$DPF" typosquat <domain> --json` |
150
+ | "Get me the trademark deeplinks." | `"$DPF" trademark <domain> --jurisdictions us,eu --json` |
151
+ | "How does this read in Spanish?" | `"$DPF" semantics <domain> --languages es --json` |
152
+ | "Is this name memorable for voice?" | `"$DPF" llmo <domain> --json` |
153
+ | "Has this domain been used before?" | `"$DPF" history <domain> --json` |
154
+ | "Is the structure valid at all?" | `"$DPF" basic <domain> --json` |
155
+
156
+ These can also be batched — same parallel pattern as multi-domain `check`.
157
+
158
+ ## Trademark output (every mode)
159
+
160
+ Every trademark check returns `status="not_supported"` with a populated `deeplink`. **This is not a bug** — see ADR 0009. The CLI no longer attempts live queries against USPTO / EUIPO / J-PlatPat because none of those registries publishes a stable, documented, no-auth search API.
161
+
162
+ When summarising:
163
+
164
+ - Single-domain mode: surface the 3 deeplinks inline (one for each jurisdiction).
165
+ - Multi-domain mode: surface the **top-3** candidates' deeplinks in the verify checklist (9 total). Skip ranks 4+.
166
+ - Always tell the user trademark verification is **manual** — clicking a deeplink opens the official search UI pre-filled with the SLD.
167
+ - Never say "no trademark conflict found" — the CLI cannot determine that.
168
+
169
+ ## Error handling
170
+
171
+ - **CLI not installed**: tell the user how to install (see install section above).
172
+ - **Network error during `--check-handles`**: re-run the same command once before reporting failure; if still failing, surface the unknown entries in the summary as `❓` and note "transport error" inline.
173
+ - **Wayback CDX timeout**: a single ReadTimeout is common; for new / freshly-registered domains, "no Wayback snapshots" is the expected outcome anyway. Note the timeout but do not block on it.
174
+ - **Domain has obvious syntactic issues** (`-foo-.com`): the `basic` check returns RED with `is_valid_syntax = false`; pass that signal up immediately rather than running the rest.
175
+
176
+ ## Exit-code conventions
177
+
178
+ `dpf check` exit codes encode the band:
179
+ - `0` — GREEN or YELLOW (proceed / proceed with caution)
180
+ - `1` — ORANGE (mitigate before proceeding)
181
+ - `2` — RED (do not register without manual review)
182
+
183
+ In a shell script, `if dpf check "$d" --json > out.json; then ...` works because both clean bands return 0. In multi-domain mode, do **not** rely on the per-call exit code to abort early — collect all results, then rank.
184
+
185
+ ## Related docs
186
+
187
+ - Full CLI walkthrough: `docs/guide/usage.md` in the repo
188
+ - Architecture: `docs/architecture.md`
189
+ - ADRs: `docs/decisions/` (especially 0009 for trademark)
190
+
191
+ ## Stylistic notes
192
+
193
+ - Mirror the user's language (English in / English out, 日本語 in / 日本語 out).
194
+ - The tool flags candidates, not legal opinions — when surfacing trademark hits, never claim infringement; say "consult counsel" if the user asks for a verdict on the legal angle.
195
+ - Treat the LLMO score as one input among many; it is permanently marked experimental.
196
+ - Multi-domain output is dense. Resist the urge to add filler explanation between the table, the ranking, and the verify checklist — the user will read them in order, not as one long paragraph.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something is wrong with the CLI or a check
4
+ title: "[bug] "
5
+ labels: bug
6
+ ---
7
+
8
+ ## What happened
9
+
10
+ A clear, single-paragraph description of the actual behaviour.
11
+
12
+ ## What I expected
13
+
14
+ What the docs or your intuition said *should* have happened.
15
+
16
+ ## Reproduction
17
+
18
+ ```bash
19
+ # the exact CLI invocation
20
+ dpf check ...
21
+ ```
22
+
23
+ If the bug is data-driven (a specific domain trips a wrong band, a brand list miss, etc.), include the domain.
24
+
25
+ ## Output
26
+
27
+ The relevant stdout / JSON. If the report is large, paste the `verdict.deductions` array and the offending check section only.
28
+
29
+ ## Environment
30
+
31
+ - domain-pre-flight version: `dpf --version`
32
+ - Python: `python --version`
33
+ - OS:
34
+ - Install method: `pipx install` / `pip install -e` / other
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new check, flag, platform, or data update
4
+ title: "[feature] "
5
+ labels: enhancement
6
+ ---
7
+
8
+ ## What you want
9
+
10
+ One paragraph describing the feature.
11
+
12
+ ## Why it matters
13
+
14
+ The decision the feature would help the user make. "Make X possible" is OK; "tool would be more complete" is not.
15
+
16
+ ## What it should NOT do
17
+
18
+ Things that are out of scope. (See README for the project's stated boundaries.)
19
+
20
+ ## How it might fit
21
+
22
+ If you have an opinion on which existing check / module / data file this slots into, mention it. The maintainer will route otherwise.
@@ -0,0 +1,22 @@
1
+ ## What this PR does
2
+
3
+ One paragraph; the imperative form ("add ...", "fix ...", "refactor ...").
4
+
5
+ ## Why
6
+
7
+ Linked issue or one-line motivation.
8
+
9
+ ## Tests
10
+
11
+ - [ ] `pytest -q` passes
12
+ - [ ] `ruff check src tests scripts` clean
13
+ - [ ] `mypy src/domain_pre_flight` clean
14
+ - [ ] `bash scripts/smoke.sh --offline` passes
15
+ - [ ] Coverage stays at ≥70% for the cli-omitted bundle
16
+
17
+ ## Checklist (delete what doesn't apply)
18
+
19
+ - [ ] No new runtime dependency added (or, if added, justified in the commit message).
20
+ - [ ] If a new check: `score.py` updated with a `_<name>_deductions` function; `cli.py` integrated.
21
+ - [ ] If a doc-bearing design choice: ADR added under `docs/decisions/`.
22
+ - [ ] CHANGELOG.md updated.
@@ -0,0 +1,40 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build-and-publish:
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ url: https://pypi.org/p/domain-pre-flight
15
+ permissions:
16
+ id-token: write
17
+ contents: read
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ with:
21
+ fetch-depth: 0
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+
28
+ - name: Install build tooling
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install build twine
32
+
33
+ - name: Build sdist + wheel
34
+ run: python -m build
35
+
36
+ - name: Verify package metadata
37
+ run: twine check dist/*
38
+
39
+ - name: Publish to PyPI (Trusted Publisher OIDC)
40
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,42 @@
1
+ name: refresh-known-brands
2
+
3
+ on:
4
+ schedule:
5
+ # First Tuesday of each month at 03:30 UTC (offset from refresh-tld-risk).
6
+ - cron: "30 3 1-7 * 2"
7
+ workflow_dispatch: {}
8
+
9
+ jobs:
10
+ refresh:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ cache: pip
23
+
24
+ - name: Install runtime deps
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install requests tldextract
28
+
29
+ - name: Refresh brand list from Tranco
30
+ run: python scripts/refresh_known_brands.py --top 2000
31
+
32
+ - name: Open PR if data changed
33
+ uses: peter-evans/create-pull-request@v6
34
+ with:
35
+ commit-message: "data: refresh known_brands.txt from Tranco"
36
+ title: "data: refresh known_brands.txt (auto, Tranco top 2000)"
37
+ body: |
38
+ Automated monthly refresh of `src/domain_pre_flight/data/known_brands.txt`.
39
+ Pulls the latest Tranco top 2000 and unions stems with the curated
40
+ baseline. Manually re-run with `python scripts/refresh_known_brands.py`.
41
+ branch: auto/known-brands-refresh
42
+ delete-branch: true
@@ -0,0 +1,41 @@
1
+ name: refresh-tld-risk
2
+
3
+ on:
4
+ schedule:
5
+ # First Monday of each month at 03:00 UTC.
6
+ - cron: "0 3 1-7 * 1"
7
+ workflow_dispatch: {}
8
+
9
+ jobs:
10
+ refresh:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ cache: pip
23
+
24
+ - name: Install runtime deps
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install requests
28
+
29
+ - name: Refresh TLD risk bundle
30
+ run: python scripts/refresh_tld_risk.py
31
+
32
+ - name: Open PR if data changed
33
+ uses: peter-evans/create-pull-request@v6
34
+ with:
35
+ commit-message: "data: refresh tld_risk.json"
36
+ title: "data: refresh tld_risk.json (auto)"
37
+ body: |
38
+ Automated monthly refresh of `src/domain_pre_flight/data/tld_risk.json`.
39
+ Re-run locally with `python scripts/refresh_tld_risk.py`.
40
+ branch: auto/tld-risk-refresh
41
+ delete-branch: true
@@ -0,0 +1,41 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Install build tooling
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install build twine
27
+
28
+ - name: Build sdist + wheel
29
+ run: python -m build
30
+
31
+ - name: Verify package metadata
32
+ run: twine check dist/*
33
+
34
+ - name: Upload to GitHub Release
35
+ env:
36
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37
+ run: |
38
+ gh release upload "${GITHUB_REF##*/}" dist/* --clobber
39
+
40
+ # PyPI publishing is handled by publish.yml (Trusted Publisher / OIDC).
41
+ # This workflow only uploads the sdist + wheel to the GitHub Release.
@@ -0,0 +1,57 @@
1
+ name: test
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
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12"]
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
+ cache: pip
24
+
25
+ - name: Install package and dev dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install -e ".[dev]"
29
+
30
+ - name: Run pytest with coverage
31
+ run: pytest --cov --cov-report=term-missing --cov-fail-under=70
32
+
33
+ - name: Run smoke tests (offline)
34
+ if: matrix.python-version == '3.12'
35
+ run: bash scripts/smoke.sh --offline
36
+
37
+ lint:
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - name: Set up Python
43
+ uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.12"
46
+ cache: pip
47
+
48
+ - name: Install dev dependencies
49
+ run: |
50
+ python -m pip install --upgrade pip
51
+ pip install -e ".[dev]"
52
+
53
+ - name: Ruff check
54
+ run: ruff check src tests scripts
55
+
56
+ - name: Mypy check
57
+ run: mypy src/domain_pre_flight
@@ -0,0 +1,22 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ env/
11
+ .env
12
+ .env.local
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+ .tox/
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+ .DS_Store
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to **domain-pre-flight** are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loosely; versions follow [SemVer](https://semver.org/) with the convention that **0.x is experimental** — breaking changes can land on a minor bump.
4
+
5
+ ## [0.9.0] — 2026-07-07
6
+
7
+ ### Added
8
+
9
+ - **MCP server** (`domain-pre-flight[mcp]`) — new optional extra and `domain-pre-flight-mcp` entry point. Ships an MCP (Model Context Protocol) server built on FastMCP so AI assistants (Claude Code / Cursor / Claude Desktop) can call the checks as tools. Exposes 4 use-case-oriented tools rather than mapping every CLI flag: `check_domain`, `check_handles`, `check_trademark`, `list_typo_permutations`.
10
+ - **Subprocess-variant reference implementation** — `examples/subprocess-variant/` ships a deliberately naive subprocess-based MCP server plus a benchmark script (`bench.py`) as the "before" half of a security / latency comparison. Not for production.
11
+ - **PyPI publishing workflow** — `.github/workflows/publish.yml` publishes to PyPI via Trusted Publisher OIDC on every `v*` tag push; no long-lived API tokens.
12
+
13
+ ### Changed
14
+
15
+ - `pyproject.toml` gains `[project.optional-dependencies].mcp = ["mcp>=1.2"]` and `[project.scripts].domain-pre-flight-mcp = "..."`. Core CLI users are unaffected — the `mcp` dependency only installs when `[mcp]` is requested.
16
+ - `release.yml` no longer holds a `PYPI_API_TOKEN` fallback path; PyPI publishing is centralised in `publish.yml`.
17
+
18
+ ## [0.8.0] — 2026-05-08
19
+
20
+ ### Added
21
+
22
+ - **IDN homograph detection** — new `dpf homograph` subcommand and `--no-homograph` flag. UTS #39 confusable matching against the brand list. `gооgle.com` (Cyrillic о) is flagged as a brand collision.
23
+ - **RDAP lifecycle check** — new `dpf rdap` subcommand and `--check-rdap` flag. Surfaces creation date, expiration date, registrar, and registry status (clientHold / redemptionPeriod / etc.) via the rdap.org public gateway.
24
+ - **DNS hygiene check** — new `dpf dns` subcommand and `--check-dns` flag. Probes MX / SPF / DMARC / DKIM presence; flags MX-without-SPF / MX-without-DMARC as deductions.
25
+ - **dnstwist-style permutations** — new `dpf permutations` subcommand. Generates 8 kinds of typo / spoof variants (omission, transposition, doubling, homoglyph, keyboard_adjacent, substitution, addition, hyphenation).
26
+ - **GitLab handle availability** — added to `dpf handles` defaults.
27
+ - **5 new languages** in semantics: Hindi (`hi`), Arabic (`ar`), Vietnamese (`vi`), Thai (`th`), Indonesian (`id`). Romanised forms only.
28
+ - **LLMO locale profile** — `--llmo-locale en|neutral`. Neutral mode relaxes the cluster ceiling so transliterated names (roomaji, pinyin) aren't unfairly penalised.
29
+ - **Tranco-driven brand list refresh** — `scripts/refresh_known_brands.py` + monthly `refresh-known-brands` workflow.
30
+ - **CI quality gates** — ruff lint job, mypy type job, pytest with `--cov-fail-under=70` (cli.py omitted from coverage).
31
+ - **Project hygiene files** — `CHANGELOG.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `.github/ISSUE_TEMPLATE/*`, `.github/pull_request_template.md`, `release.yml` workflow.
32
+
33
+ ### Changed
34
+
35
+ - **Trademark module** — every jurisdiction now resolves to `not_supported` + deeplink (no live API queries). See ADR 0009. Doc drift in `CLAUDE.md`, `docs/architecture.md`, and `docs/guide/usage.md` swept to match.
36
+ - **Skill** — `argument-hint` accepts multiple domains; multi-domain output spec added (comparison table → ranking → top-3 trademark verify checklist → next step).
37
+
38
+ ### Fixed
39
+
40
+ - `_de_confuse` previously rewrote ASCII characters into other confusables; now only non-Latin characters are de-confused (preserves Latin SLDs unchanged).
41
+
42
+ ### Internal
43
+
44
+ - `basic.normalise(domain) → (domain, sld, tld)` shared helper, replaces 5 duplicated normalisation passes across check modules.
45
+ - Ten check modules now share consistent shapes (`check_<name>(domain) → <Name>Report` returning `issues` + `notes`).
46
+
47
+ ## [0.7.2] — 2026-05-08
48
+
49
+ ### Changed
50
+
51
+ - Skill multi-domain mode + top-3 trademark verify checklist (9 links instead of 24).
52
+
53
+ ## [0.7.0] — 2026-05-08
54
+
55
+ ### Added
56
+
57
+ - ROADMAP issues #1–#6 all shipped: same-name handle availability, typosquat / brand similarity, trademark deeplinks, multi-language semantics, refreshable TLD risk JSON, LLMO fitness heuristic.
58
+
59
+ ## [0.1.0] — 2026-05-08
60
+
61
+ ### Added
62
+
63
+ - Initial public release. `dpf check / basic / history`, scoring engine, GREEN / YELLOW / ORANGE / RED bands.