Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
1ed0247
fix: the npm publish job published the wrapper twice
sotashimozono Aug 29, 2026
e112757
Merge pull request #558 from QAtlasHub/fix/npm-publish-duplicate
sotashimozono Aug 29, 2026
6c4fa6b
Merge pull request #559 from QAtlasHub/main
sotashimozono Aug 29, 2026
98b8daf
feat: the Claude Code plugin no longer requires doiget to be installe…
sotashimozono Aug 29, 2026
ec430b5
Merge pull request #560 from QAtlasHub/feat/plugin-npx
sotashimozono Aug 29, 2026
a83b893
docs: four tools shipped undocumented, and nothing compared the table…
sotashimozono Aug 29, 2026
24cb9fd
Merge pull request #561 from QAtlasHub/fix/mcp-tools-doc-drift
sotashimozono Aug 29, 2026
45bc63e
fix(mcp): a zero-result search read as "this paper does not exist"
sotashimozono Aug 29, 2026
042e379
Merge remote-tracking branch 'origin/next' into fix/paper-search-zero…
sotashimozono Aug 30, 2026
fed27ae
Merge pull request #562 from QAtlasHub/fix/paper-search-zero-hint
sotashimozono Aug 30, 2026
97a7d06
perf(ci): one test took ten minutes and was being run five times
sotashimozono Aug 30, 2026
c6c2f47
Merge pull request #563 from QAtlasHub/perf/batch-e2e-virtual-time
sotashimozono Aug 30, 2026
acbca82
fix(mcp): oa_url was documented as actionable and was always null
sotashimozono Aug 30, 2026
af12c8e
fix(test): typos flagged 'optin' in a log filename
sotashimozono Aug 30, 2026
0221d84
fix(release): the beta bump missed the three workspace dependency pins
sotashimozono Aug 30, 2026
1b80c55
fix(fetch): a DOI resolver is addressing, not a content host
sotashimozono Aug 30, 2026
39b0797
Merge pull request #564 from QAtlasHub/feat/oa-location-opt-in
sotashimozono Aug 30, 2026
7f38689
chore: merge next (0.8.11 -> beta.6) and re-bump to beta.7
sotashimozono Aug 30, 2026
09c7d9d
feat(cli): the found-nothing fetch says what it consulted
sotashimozono Aug 30, 2026
8382ec1
fix(docs): rustdoc private link and a typos hit
sotashimozono Aug 30, 2026
daffeb5
Merge pull request #565 from QAtlasHub/fix/533-resolver-hop-is-addres…
sotashimozono Aug 30, 2026
432fc90
Merge remote-tracking branch 'origin/next' into feat/505-found-nothin…
sotashimozono Aug 30, 2026
8fc663f
chore: merge next (#533) and re-bump to beta.8
sotashimozono Aug 30, 2026
5a41708
Merge pull request #566 from QAtlasHub/feat/505-found-nothing-trace
sotashimozono Aug 30, 2026
381fa99
fix(core): an access refusal was decided by substring, so rewording one
sotashimozono Aug 30, 2026
be64c2f
Merge pull request #567 from QAtlasHub/fix/538-typed-access-refusal
sotashimozono Aug 30, 2026
d8d9e7d
feat(mcp): a failure says what to do about it, not just what happened
sotashimozono Aug 30, 2026
cceb82d
chore: merge next (#538) and re-bump to beta.10
sotashimozono Aug 30, 2026
4747a7b
Merge pull request #568 from QAtlasHub/feat/506-error-disposition
sotashimozono Aug 30, 2026
954e160
feat(mcp): a citation candidate says whether it is an identity or a
sotashimozono Aug 30, 2026
a327fa5
chore: merge next (#506) and re-bump to beta.11
sotashimozono Aug 30, 2026
a74f833
Merge pull request #569 from QAtlasHub/feat/536-citation-confidence
sotashimozono Aug 30, 2026
cca1beb
fix(fetch): OpenAlex named the repository; the run said nothing was
sotashimozono Aug 30, 2026
66253c7
Merge remote-tracking branch 'origin/next' into feat/547-openalex-loc…
sotashimozono Aug 30, 2026
673d21d
chore: merge next (#536) and re-bump to beta.12
sotashimozono Aug 31, 2026
c1d4053
Merge pull request #570 from QAtlasHub/feat/547-openalex-location-trace
sotashimozono Aug 31, 2026
86b8d10
feat(dist): Homebrew, generated from the release's own checksums
sotashimozono Aug 31, 2026
325db28
Merge remote-tracking branch 'origin/next' into feat/501-homebrew-tap
sotashimozono Aug 31, 2026
7ec4814
chore: merge next (#547) and re-bump to beta.13
sotashimozono Aug 31, 2026
db6b89f
Merge pull request #571 from QAtlasHub/feat/501-homebrew-tap
sotashimozono Aug 31, 2026
508ea28
fix(provenance): the bookend recorded that a call failed, not what with
sotashimozono Aug 31, 2026
f99a265
Merge remote-tracking branch 'origin/next' into feat/507-log-the-call…
sotashimozono Aug 31, 2026
e4f7e7f
chore: merge next (#501) and re-bump to beta.14
sotashimozono Aug 31, 2026
61d53aa
Merge pull request #572 from QAtlasHub/feat/507-log-the-call-outcome
sotashimozono Aug 31, 2026
7f9347c
fix(mcp): the disposition was missing from the five envelopes that
sotashimozono Aug 31, 2026
b8b11c9
chore: merge next (#507 step 1) and re-bump to beta.15
sotashimozono Aug 31, 2026
c9cd7fa
Merge pull request #573 from QAtlasHub/feat/506-the-rest
sotashimozono Aug 31, 2026
c67b4e9
feat(cli): rank the sources not consulted, and say which part is a guess
sotashimozono Aug 31, 2026
29c00ea
Merge pull request #574 from QAtlasHub/feat/505-rank-the-sources
sotashimozono Aug 31, 2026
e268e1c
feat(mcp): the server's Retry-After reaches the caller
sotashimozono Aug 31, 2026
54544cb
fix(bib): a PubMed entry was reported as having no identifier; it has a
sotashimozono Aug 31, 2026
5f0ac3e
test(e2e): record which PDF route each test asserts, because almost none
sotashimozono Aug 31, 2026
473c1d7
Merge pull request #577 from QAtlasHub/test/462-route-coverage
sotashimozono Aug 31, 2026
c7eb591
test(e2e): close three of the four route gaps, and reproduce the fourth
sotashimozono Aug 31, 2026
ee10d54
fix: eleven defects the promotion review found, most of them my own
sotashimozono Aug 31, 2026
23be753
Merge pull request #582 from QAtlasHub/fix/review-580-followups
sotashimozono Aug 31, 2026
74ac622
chore: merge next and re-bump to beta.19
sotashimozono Aug 31, 2026
ee1dd2d
fix(ci): the citation feature never compiled, and my local runs never…
sotashimozono Aug 31, 2026
bc5edb5
chore: commit the beta bump and re-sync site/content
sotashimozono Aug 31, 2026
68bd7cf
Merge pull request #575 from QAtlasHub/feat/506-retry-after
sotashimozono Aug 31, 2026
c82f9f5
Merge remote-tracking branch 'origin/next' into fix/citation-build-or…
sotashimozono Aug 31, 2026
1ebfe5b
Merge pull request #584 from QAtlasHub/fix/citation-build-orphaned
sotashimozono Aug 31, 2026
a139802
chore(deps): the three pending Dependabot bumps, in one pass
sotashimozono Aug 31, 2026
5843eb6
Merge remote-tracking branch 'origin/next' into fix/500-say-which-ide…
sotashimozono Aug 31, 2026
5e54edd
chore(supply-chain): put zlib-rs in the order cargo-vet demands
sotashimozono Aug 31, 2026
00b7329
Merge pull request #585 from QAtlasHub/chore/pending-dependency-bumps
sotashimozono Aug 31, 2026
4f9be01
Merge remote-tracking branch 'origin/next' into fix/500-say-which-ide…
sotashimozono Aug 31, 2026
8e97fac
fix(store): a not-determined marker no longer overwrites a determination
sotashimozono Aug 31, 2026
6dbc271
fix(bib): one definition of the claim, and the whitespace three copie…
sotashimozono Aug 31, 2026
c90863b
style: rustfmt the rewired MCP call site
sotashimozono Aug 31, 2026
5aeef76
Merge pull request #576 from QAtlasHub/fix/500-say-which-identifier
sotashimozono Aug 31, 2026
ebfdc67
Merge remote-tracking branch 'origin/next' into fix/583-store-downgrade
sotashimozono Aug 31, 2026
a4265cc
Merge pull request #586 from QAtlasHub/fix/583-store-downgrade
sotashimozono Aug 31, 2026
e197a95
fix: close the review findings on #580, and cut 0.8.13
Sep 1, 2026
5cfcd5b
fix(release): bump the inter-crate version requirements too
Sep 1, 2026
3e5ea17
fix(ci): make the release-sync check say why it failed, and catch eve…
Sep 1, 2026
316c263
fix: close the review findings on this PR itself
Sep 1, 2026
9ee9baa
test: cover the tools that had no tests at all
Sep 1, 2026
e60d506
Merge pull request #588 from QAtlasHub/fix/580-review-and-cut
sotashimozono Sep 1, 2026
d51df16
fix: stop the release publishing a wrapper alone, and stop a fetch ea…
Sep 1, 2026
08e049f
fix: one rate limiter and one provenance log per process, not per too…
Sep 1, 2026
d66dbb6
fix: write the resolver cache the way the store next door writes
Sep 1, 2026
9a5c8c6
Merge pull request #589 from QAtlasHub/fix/580-review-round-3
sotashimozono Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "doiget",
"metadata": {
"description": "The doiget marketplace — one plugin: an OA-first paper fetcher for DOIs and arXiv IDs.",
"version": "0.8.11"
"version": "0.8.12"
},
"owner": {
"name": "QAtlasHub",
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "doiget",
"description": "Turn DOIs and arXiv IDs into local PDFs and structured metadata via official Open-Access APIs. Never bypasses paywalls.",
"version": "0.8.11",
"version": "0.8.12",
"author": {
"name": "Sota Shimozono",
"url": "https://github.com/QAtlasHub/doiget"
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ jobs:
# so test and clippy exercise an identical feature surface.
- run: cargo test --workspace --all-targets --no-default-features --features oa-only

# The five jobs above skip `#[ignore]`d tests, which is where the one
# 10-minute test lives. It is a dispatch-loop property that varies with
# neither OS nor Cargo feature, so running it in all five spent ~50 minutes
# per CI run learning the same thing five times. Once is enough.
#
# NOT a sixth leg of the `test` matrix: it has to run in PARALLEL with the
# others rather than lengthen one of them, and it needs no OS spread.
test-slow:
name: test (slow)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
cache-bin: false
# `--ignored` runs ONLY the ignored tests, so this job is exactly the
# slow set and the five broad jobs are exactly everything else. Nothing
# is dropped by the split and nothing is run twice.
- run: cargo test --workspace --all-targets --no-default-features --features oa-only -- --ignored

test-citation:
name: test (citation feature)
runs-on: ubuntu-latest
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Initialize CodeQL
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
languages: ${{ matrix.language }}

- name: Autobuild
uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
category: "/language:${{ matrix.language }}"
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
with:
components: llvm-tools-preview
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: taiki-e/install-action@ba47c86ac325773530516bb756137ac718732518 # v2.86.5
- uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0
with:
tool: cargo-llvm-cov
- name: Generate workspace coverage (lcov)
Expand Down
166 changes: 166 additions & 0 deletions .github/workflows/posture-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,45 @@ jobs:
fi
echo "LICENSE OK: verbatim SPDX MIT, no trailing prose, ends at SOFTWARE., LF."

- name: MCP_TOOLS.md lists exactly the tools the router exposes (#553)
shell: bash
run: |
set -euo pipefail
# `MCP_TOOLS.md` is what an integrator reads to decide what the server
# can do, and `docs/INTEGRATION/*.md` points at it. Nothing checked it
# against the router, and it had drifted in BOTH directions at once:
# four tools shipped with no entry (#553), and one had an entry while
# not shipping at all (#552). Neither is visible from inside the other
# file.
#
# rmcp derives a tool's name from its `#[tool]` method name, so the
# method list is the router's own truth. Compared against the table
# rows, which are the document's.
fns="$(grep -oE '^[[:space:]]+(pub )?async fn doiget_[a-z_0-9]+' \
crates/doiget-mcp/src/lib.rs \
| grep -oE 'doiget_[a-z_0-9]+' | sort -u)"
rows="$(grep -oE '^\| .doiget_[a-z_0-9]+.' docs/MCP_TOOLS.md \
| grep -oE 'doiget_[a-z_0-9]+' | sort -u)"
if [ -z "$fns" ] || [ -z "$rows" ]; then
echo "::error::posture-lint mcp-tools: read no tools from the router or no rows from MCP_TOOLS.md — one of the two patterns has stopped matching (#553)"
exit 1
fi
missing="$(comm -23 <(printf '%s\n' "$fns") <(printf '%s\n' "$rows"))"
extra="$(comm -13 <(printf '%s\n' "$fns") <(printf '%s\n' "$rows"))"
fail=0
if [ -n "$missing" ]; then
echo "::error::posture-lint mcp-tools: these tools ship but have no row in docs/MCP_TOOLS.md (#553)"
printf ' %s\n' $missing
fail=1
fi
if [ -n "$extra" ]; then
echo "::error::posture-lint mcp-tools: docs/MCP_TOOLS.md documents these, but the router has no such tool (#552)"
printf ' %s\n' $extra
fail=1
fi
[ "$fail" -eq 0 ]
echo "MCP_TOOLS.md and the router agree on $(printf '%s\n' "$fns" | wc -l | tr -d ' ') tools"

- name: doiget-cli forwards every doiget-mcp feature (#373 / #516)
shell: bash
run: |
Expand Down Expand Up @@ -244,6 +283,82 @@ jobs:
shell: bash
run: bash npm/doiget-cli/test/release-checksums.test.sh

- name: Homebrew formula is generator output (#501)
shell: bash
# `Formula/doiget.rb` is generated from a release's published `.sha256`
# assets. #247 was closed as completed while four fifths of it had not
# shipped, so the formula is asserted rather than trusted.
#
# What this fails on: a hand-edit, a MALFORMED checksum, a `v`-prefixed
# version. What it CANNOT fail on, because it re-reads the version and
# the checksums from the very file under test: a well-formed checksum
# carrying the wrong value, or a version that is simply stale. It said
# it caught "a bad checksum" and it does not -- nothing here reaches the
# published `.sha256` assets, offline being the point. The stale-version
# half is covered by the release-sync check below; a wrong-value
# checksum is caught by `brew install` and by nothing before it.
run: bash scripts/update-homebrew-formula.test.sh

- name: release-tracking files name one version (#501, #511)
shell: bash
# Four files record "the last published stable", and a stable release
# bumps all four in one maintainer step. Nothing used to hold them
# together, which is how `.claude-plugin/*` sat at 0.8.11 while 0.8.12
# was the shipped release.
#
# What this CANNOT catch, stated so the check does not repeat the
# overclaim it exists to fix: forgetting the step ENTIRELY leaves all
# four at the previous release, mutually consistent, and green.
# Nothing in the repository knows which version is currently published.
# What it does catch is bumping some and not others, and pinning a
# version that was never released -- CHANGELOG.md is the only
# independent source of truth available offline.
run: |
set -euo pipefail
# `|| true` on each is load-bearing. Under `set -euo pipefail` a bare
# assignment takes the exit status of its command substitution, so a
# `grep` that matches nothing -- somebody deletes the `@version` pin from
# .mcp.json, or reformats a manifest -- kills the step HERE, before the
# `::error::` written to explain it. It failed closed, which is right, and
# said nothing, which is the defect this whole job exists to catch. Found
# by review of this change; the same shape was fixed one file over in
# npm/doiget-cli/test/stage-npm.test.sh.
formula=$(grep -oE '^ version "[^"]+"' Formula/doiget.rb | grep -oE '[0-9][^"]*' || true)
plugin=$(grep -oE '"version": *"[^"]+"' .claude-plugin/plugin.json | grep -oE '[0-9][^"]*' || true)
market=$(grep -oE '"version": *"[^"]+"' .claude-plugin/marketplace.json | grep -oE '[0-9][^"]*' || true)
pinned=$(grep -oE '"doiget-cli@[^"]+"' .mcp.json | grep -oE '[0-9][^"]*' || true)
for pair in "Formula/doiget.rb:$formula" ".claude-plugin/plugin.json:$plugin" ".claude-plugin/marketplace.json:$market" ".mcp.json:$pinned"; do
if [ -z "${pair#*:}" ]; then
echo "::error::posture-lint release-sync: no version found in ${pair%%:*} -- the file was reformatted, or .mcp.json lost its doiget-cli@<version> pin"
exit 1
fi
done
echo "formula=$formula plugin=$plugin marketplace=$market .mcp.json=$pinned"
for v in "$plugin" "$market" "$pinned"; do
if [ "$v" != "$formula" ]; then
echo "::error::posture-lint release-sync: release-tracking files disagree. Bump Formula/doiget.rb, .claude-plugin/plugin.json, .claude-plugin/marketplace.json and .mcp.json together (CONTRIBUTING.md, 'one step after a stable release')"
exit 1
fi
done
# An unpublished pin would send every plugin user to a 404. Any `-`
# suffix is a prerelease per SemVer; the first version of this listed
# spellings (`*-beta.*|*-rc.*`) and let `0.9.0-alpha.2` straight
# through, which is the enumerate-the-known-cases mistake this
# release is otherwise about.
case "$pinned" in
*-*)
echo "::error::posture-lint release-sync: .mcp.json pins a prerelease ($pinned); the plugin must point at a published stable"
exit 1
;;
esac
# The one independent check available without a network: a version
# these files claim to track must have a released CHANGELOG section.
if ! grep -qE "^## \[$formula\]" CHANGELOG.md; then
echo "::error::posture-lint release-sync: the tracking files name $formula, which has no '## [$formula]' section in CHANGELOG.md -- they point at a version that was never released"
exit 1
fi
echo "release-tracking files agree on $formula, and CHANGELOG.md has its section"

- name: npm packaging tests (#511)
shell: bash
# The name-list greps above cannot catch a wrong binary name, a
Expand All @@ -266,6 +381,57 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Every hand-built error object carries a disposition (#506)
shell: bash
run: |
set -euo pipefail
# `error_object` builds most failure envelopes, but five are assembled
# field-by-field with `insert("code", ...)` because they also attach a
# `denial_context`. The first pass at #506 converted the former and
# missed the latter -- including the two most common failures an agent
# sees -- so `disposition` shipped present on some failures and absent
# on others, which is worse than absent everywhere.
#
# One `insert("disposition"` per `insert("code"`. Not a proof, but it
# fails on exactly the mistake that was made.
f=crates/doiget-mcp/src/lib.rs
# `|| true`: `grep -c` exits 1 on a zero count, and under
# `set -euo pipefail` that kills the step at the assignment, before
# the `::error::` written below to explain it. Same guard as the
# release-sync step and `capture()`.
codes="$(grep -c 'insert("code"' "$f" || true)"
disps="$(grep -c 'insert("disposition"' "$f" || true)"
if [ "$codes" != "$disps" ]; then
echo "::error::posture-lint: $codes hand-built error objects but $disps dispositions in $f - a failure envelope is missing error.disposition (#506)"
grep -n 'insert("code"' "$f"
exit 1
fi

- name: Host adjudication uses `permits`, never `matches` (#533)
shell: bash
run: |
set -euo pipefail
# `SourceAllowlist::matches` answers "is this host on the list".
# `SourceAllowlist::permits` answers "may we go here", which is what
# every gate is actually asking -- and it is the one that treats a
# DOI resolver as addressing rather than as a content host.
#
# #533 was a gate that asked the first question and acted on the
# answer: a gold-OA cc-by paper was refused at `doi.org`, one hop
# before the publisher whose host was already allowlisted. There
# were FIVE such gates and only one of them was ever walked end to
# end, which is #462's point exactly ("every 'unreachable source'
# bug passed its unit tests").
#
# The discriminator: adjudication passes a host VARIABLE
# (`.matches(&host)`); list-membership assertions in tests pass a
# LITERAL (`.matches("doaj.org")`). So a `.matches(&` anywhere is a
# gate that should be a `permits`.
if grep -rn --include='*.rs' '\.matches(&' crates/ ; then
echo "::error::posture-lint: host adjudication must call permits(), not matches() - see http::is_transparent_resolver (#533)"
exit 1
fi

- name: No outbound-network APIs in unit / integration tests
shell: bash
run: |
Expand Down
42 changes: 40 additions & 2 deletions .github/workflows/release-plz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,45 @@ jobs:
# `EALLOWGIT: Refusing to fetch "github:npm-stage/doiget-darwin-arm64"`
# and the job died before reaching the registry at all. A path spec
# has to look like a path.
for p in ./npm-stage/doiget-*; do
npm publish "$p" --provenance --access public --tag "$DIST_TAG"
# The platform list comes from `stage-npm.sh`'s MAP, not from a
# `doiget-*` glob. The glob used to be safe because the wrapper was
# named `doiget`; renaming it to `doiget-cli` made the glob match it
# too, so v0.8.12 published the wrapper inside the loop AND again on
# the line below -- `npm error You cannot publish over the previously
# published versions: 0.8.12`, after everything had in fact shipped.
# It also sorted first, so the wrapper went out ahead of the packages
# it pins: exactly the window the paragraph above warns about.
#
# Reading the MAP cannot drift the same way. It lists platform
# packages only, so the wrapper is excluded by construction rather
# than by a name test somebody has to remember to update.
#
# The empty case is guarded, and that is not defensive noise. The
# pipeline ends in `sort -u`, which exits 0 on empty input, so
# `set -euo pipefail` gives NOTHING here: a grep that matches
# nothing (a reformat of the MAP block, a delimiter change) yields
# an empty word list, the loop runs zero times, and the step falls
# through to publish the wrapper alone -- green, with every platform
# binary silently missing. `npm install doiget-cli` would then
# succeed while its optionalDependencies fail to resolve.
#
# The `doiget-*` glob this replaced failed LOUDLY on no-match (the
# unexpanded pattern reached `npm publish` as a path that does not
# exist). Trading that for silence in the step that performs the
# irreversible publish is the wrong direction. posture-lint's
# `capture()` already guards the identical grep for the identical
# reason; it was applied to the check and not to the publish.
pkgs="$(grep -oE '^doiget-[a-z0-9-]+:' scripts/stage-npm.sh | tr -d ':' | sort -u || true)"
if [ -z "$pkgs" ]; then
echo "::error::release: no platform packages found in scripts/stage-npm.sh -- refusing to publish the wrapper alone"
exit 1
fi
count=$(echo "$pkgs" | wc -l | tr -d " ")
if [ "$count" -ne 4 ]; then
echo "::error::release: expected 4 platform packages, found $count: $(echo $pkgs)"
exit 1
fi
for pkg in $pkgs; do
npm publish "./npm-stage/$pkg" --provenance --access public --tag "$DIST_TAG"
done
npm publish ./npm-stage/doiget-cli --provenance --access public --tag "$DIST_TAG"
2 changes: 1 addition & 1 deletion .github/workflows/typos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: crate-ci/typos@8a48f81b6c64dcfea44b3633223084c4be58ac5f # v1.49.0
- uses: crate-ci/typos@4d9c206a77c041268485162b8e2579ad7a5cb9a3 # v1.50.0
4 changes: 3 additions & 1 deletion .mcp.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"mcpServers": {
"doiget": {
"command": "doiget",
"command": "npx",
"args": [
"-y",
"doiget-cli@0.8.12",
"serve"
]
}
Expand Down
Loading
Loading