citeclaw 2.0.7 → 2.0.9

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.
package/README.md CHANGED
@@ -15,7 +15,7 @@ It is designed for two related jobs:
15
15
  - an HTTP service compatible with Citoid-style API flows
16
16
  - an MCP server mode for agent/tool integrations
17
17
  - Zotero automation for query, cite, add, update, notes, dedup, enrichment, and export
18
- - vendored translator and CSL style assets for reproducible local runs
18
+ - optional translator/style sync commands for broader coverage and local CSL rendering
19
19
 
20
20
  ## Fast Start
21
21
 
@@ -120,7 +120,7 @@ Safety defaults:
120
120
 
121
121
  ## Runtime Sync
122
122
 
123
- For npm installs, extra runtime assets can be synced explicitly when you want broader coverage or local style rendering:
123
+ For npm installs, extra runtime assets can be synced explicitly when you want broader translator coverage or local style rendering:
124
124
 
125
125
  ```bash
126
126
  npx citeclaw translators sync
@@ -131,6 +131,8 @@ Notes:
131
131
 
132
132
  - `translators sync` will clone or update the translator sources with `git` when needed
133
133
  - `styles sync` will clone or update style repositories with `git` when local CSL styles are unavailable
134
+ - normal `cite` commands can run without manually syncing translators first
135
+ - `cite-style` will fetch style sources on demand if local styles are missing
134
136
  - if `git` is not installed, the commands fail with an explicit message
135
137
 
136
138
  ## Bibliography Curation
@@ -163,6 +165,23 @@ Current package metadata and entrypoints live in:
163
165
  - [package.json](/mnt/e/botcite/package.json)
164
166
  - [scripts/citeclaw.js](/mnt/e/botcite/scripts/citeclaw.js)
165
167
 
168
+ ## npm Publishing
169
+
170
+ This repo includes a GitHub Actions workflow for npm publishing:
171
+
172
+ - workflow file: `.github/workflows/npm-publish.yml`
173
+ - trigger: push a tag like `v2.0.8` that matches `package.json`'s `version`
174
+ - required secret: add `NPM_TOKEN` in GitHub repository settings with publish permission for the `citeclaw` package
175
+
176
+ Typical release flow:
177
+
178
+ ```bash
179
+ npm version patch
180
+ git push origin master --follow-tags
181
+ ```
182
+
183
+ The workflow installs dependencies with `npm ci`, checks that the tag matches `package.json`, validates the tarball with `npm pack --dry-run`, and then runs `npm publish --access public --provenance`.
184
+
166
185
  ## Notes
167
186
 
168
187
  - The npm package name and primary CLI are `citeclaw`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citeclaw",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Citation and bibliography toolkit for DOI, URL, arXiv, PDF, Zotero, and MCP workflows.",
5
5
  "homepage": "https://github.com/trotsky1997/citeclaw",
6
6
  "license": "Apache-2.0",
@@ -469,6 +469,46 @@ function runCommandOrThrow( command, args, cwd ) {
469
469
  return result;
470
470
  }
471
471
 
472
+ function styleLocaleUrls( locale ) {
473
+ const fileName = `locales-${ locale }.xml`;
474
+ return [
475
+ `https://raw.githubusercontent.com/citation-style-language/locales/master/${ fileName }`,
476
+ `https://github.com/citation-style-language/locales/raw/master/${ fileName }`,
477
+ `https://cdn.jsdelivr.net/gh/citation-style-language/locales@master/${ fileName }`
478
+ ];
479
+ }
480
+
481
+ function downloadFileWithCurlFallbacks( urls, dest, label, runner ) {
482
+ const runCommand = runner || runCommandOrThrow;
483
+ const candidates = Array.isArray( urls ) ? urls : [ urls ];
484
+ const targetLabel = label || path.basename( dest );
485
+ const tempDest = `${ dest }.tmp`;
486
+ const failures = [];
487
+ fs.mkdirSync( path.dirname( dest ), { recursive: true } );
488
+
489
+ for ( const url of candidates ) {
490
+ try {
491
+ fs.rmSync( tempDest, { force: true } );
492
+ runCommand( 'curl', [
493
+ '-fsSL',
494
+ '--retry', '3',
495
+ '--retry-delay', '1',
496
+ '--retry-all-errors',
497
+ url,
498
+ '-o', tempDest
499
+ ] );
500
+ fs.rmSync( dest, { force: true } );
501
+ fs.renameSync( tempDest, dest );
502
+ return;
503
+ } catch ( error ) {
504
+ failures.push( `${ url }: ${ error.message }` );
505
+ }
506
+ }
507
+
508
+ fs.rmSync( tempDest, { force: true } );
509
+ throw new Error( `failed to download ${ targetLabel }: ${ failures.join( ' | ' ) }` );
510
+ }
511
+
472
512
  function resolveInstallCommand() {
473
513
  if ( commandExists( 'npm' ) ) {
474
514
  return { command: 'npm', args: [ 'install' ] };
@@ -1504,19 +1544,24 @@ async function runCitationFromPdf( pdfPath, options ) {
1504
1544
  } );
1505
1545
  }
1506
1546
 
1507
- function syncStyles( options ) {
1547
+ function syncStyles( options, dependencies ) {
1548
+ const styleOptions = options || {};
1549
+ const hooks = dependencies || {};
1550
+ const downloadFile = hooks.downloadFile || downloadFileWithCurlFallbacks;
1551
+ const downloadUsesCurl = downloadFile === downloadFileWithCurlFallbacks;
1552
+
1508
1553
  ensureDirs();
1509
1554
  fs.mkdirSync( cslDir, { recursive: true } );
1510
1555
  fs.mkdirSync( localeDir, { recursive: true } );
1511
- const configuredSourceDirs = options.repo ?
1556
+ const configuredSourceDirs = styleOptions.repo ?
1512
1557
  [
1513
- path.isAbsolute( options.repo || '' ) ?
1514
- options.repo :
1515
- path.join( rootDir, options.repo || 'vendor/styles' )
1558
+ path.isAbsolute( styleOptions.repo || '' ) ?
1559
+ styleOptions.repo :
1560
+ path.join( rootDir, styleOptions.repo || 'vendor/styles' )
1516
1561
  ] :
1517
1562
  ensureDefaultStyleSources();
1518
1563
  const sourceDirs = configuredSourceDirs.filter( ( sourceDir ) => fileExists( sourceDir ) );
1519
- if ( options.repo && !sourceDirs.length ) {
1564
+ if ( styleOptions.repo && !sourceDirs.length ) {
1520
1565
  throw new Error( `styles source not found: ${ configuredSourceDirs.join( ', ' ) }` );
1521
1566
  }
1522
1567
 
@@ -1532,13 +1577,12 @@ function syncStyles( options ) {
1532
1577
  } );
1533
1578
 
1534
1579
  const localeTargets = [ 'en-US', 'zh-CN' ];
1535
- if ( !commandExists( 'curl' ) ) {
1580
+ if ( downloadUsesCurl && !commandExists( 'curl' ) ) {
1536
1581
  throw new Error( 'curl is required for styles sync' );
1537
1582
  }
1538
1583
  for ( const locale of localeTargets ) {
1539
- const localeUrl = `https://raw.githubusercontent.com/citation-style-language/locales/master/locales-${ locale }.xml`;
1540
1584
  const dest = path.join( localeDir, `locales-${ locale }.xml` );
1541
- runCommandOrThrow( 'curl', [ '-fsSL', localeUrl, '-o', dest ] );
1585
+ downloadFile( styleLocaleUrls( locale ), dest, `locale ${ locale }` );
1542
1586
  }
1543
1587
 
1544
1588
  process.stdout.write( `styles synced to ${ cslDir } from ${ sourceDirs.join( ', ' ) } (copied ${ copiedCount } files)\n` );
@@ -4979,9 +5023,11 @@ function main() {
4979
5023
  usage();
4980
5024
  process.exit( 1 );
4981
5025
  }
4982
- syncStyles( parsed ).catch( ( error ) => {
5026
+ try {
5027
+ syncStyles( parsed );
5028
+ } catch ( error ) {
4983
5029
  handleCommandError( error, parsed, 'styles', 'sync' );
4984
- } );
5030
+ }
4985
5031
  return;
4986
5032
  }
4987
5033
 
@@ -5140,12 +5186,14 @@ function main() {
5140
5186
  }
5141
5187
 
5142
5188
  module.exports = {
5189
+ downloadFileWithCurlFallbacks,
5143
5190
  detectPdfIdentifierCandidates,
5144
5191
  extractBestDoiCandidate,
5145
5192
  extractPdfCandidates,
5146
5193
  main,
5147
5194
  normalizeArxivId,
5148
5195
  normalizeDoi,
5196
+ syncStyles,
5149
5197
  shouldAttemptPdfOcr
5150
5198
  };
5151
5199