Skip to content

Commit 6f4aba7

Browse files
committed
docs: auto-sync domain tables from OpenAPI spec
- Add scripts/sync-docs.ts to regenerate the domains table in README.md and AGENTS.md between HTML markers, driven by scripts/tag-descriptions.ts. Fails loudly on unknown tags, stale entries, or missing llms-full.txt sections. - Wire into bun run generate, lefthook pre-commit (auto-stage re-synced files), lefthook pre-push (drift gate), and release.yml (runs before the spec-diff check). - Document biorhythm domain (6 endpoints) missed in last 2 releases, and add multi-language note (8 languages) to README.md, AGENTS.md, and docs/llms-full.txt.
1 parent ddc7af6 commit 6f4aba7

9 files changed

Lines changed: 338 additions & 26 deletions

File tree

.github/workflows/release.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ jobs:
3535
- name: Regenerate SDK
3636
run: bun run generate
3737

38+
- name: Sync docs from spec
39+
run: bun run docs:sync
40+
3841
- name: Check if spec changed
3942
id: diff
4043
run: |
@@ -74,7 +77,7 @@ jobs:
7477
git config user.name "github-actions[bot]"
7578
git config user.email "github-actions[bot]@users.noreply.github.com"
7679
VERSION=$(node -p "require('./package.json').version")
77-
git add package.json src/version.ts specs/openapi.json src/
80+
git add package.json src/version.ts specs/openapi.json src/ README.md AGENTS.md
7881
git commit -m "release: v$VERSION"
7982
git tag "v$VERSION"
8083
git push --follow-tags

AGENTS.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,23 @@ const roxy = createRoxy(process.env.ROXY_API_KEY!);
2222

2323
Type `roxy.` to see all available namespaces. Type `roxy.{domain}.` to see every method in that domain.
2424

25-
| Namespace | What it covers |
26-
|-----------|----------------|
27-
| `roxy.astrology` | Western astrology: natal charts, horoscopes, synastry, moon phases, transits, compatibility |
28-
| `roxy.vedicAstrology` | Vedic/Jyotish: birth charts, dashas, nakshatras, panchang, KP system, doshas, yogas |
29-
| `roxy.tarot` | Rider-Waite-Smith deck: spreads, daily pulls, yes/no, Celtic Cross, custom layouts |
30-
| `roxy.numerology` | Life path, expression, soul urge, personal year, karmic analysis, compatibility |
31-
| `roxy.crystals` | Crystal healing properties, zodiac/chakra pairings, birthstones, search |
32-
| `roxy.iching` | I Ching: hexagrams, trigrams, coin casting, daily readings |
33-
| `roxy.angelNumbers` | Angel number meanings, pattern analysis, daily guidance |
34-
| `roxy.dreams` | Dream symbol dictionary and interpretations |
35-
| `roxy.location` | City geocoding for birth chart coordinates |
36-
| `roxy.usage` | API usage stats and subscription info |
25+
<!-- BEGIN:DOMAINS -->
26+
| Namespace | Endpoints | What it covers |
27+
|-----------|-----------|----------------|
28+
| `roxy.astrology` | 22 | Western astrology: natal charts, horoscopes, synastry, moon phases, transits, compatibility |
29+
| `roxy.vedicAstrology` | 42 | Vedic/Jyotish: birth charts, dashas, nakshatras, panchang, KP system, doshas, yogas |
30+
| `roxy.tarot` | 10 | Rider-Waite-Smith deck: spreads, daily pulls, yes/no, Celtic Cross, custom layouts |
31+
| `roxy.numerology` | 16 | Life path, expression, soul urge, personal year, karmic analysis, compatibility |
32+
| `roxy.dreams` | 5 | Dream symbol dictionary and interpretations |
33+
| `roxy.angelNumbers` | 4 | Angel number meanings, pattern analysis, daily guidance |
34+
| `roxy.iching` | 9 | I Ching: hexagrams, trigrams, coin casting, daily readings |
35+
| `roxy.crystals` | 12 | Crystal healing properties, zodiac/chakra pairings, birthstones, search |
36+
| `roxy.biorhythm` | 6 | 10-cycle biorhythm readings, forecasts, critical days, compatibility, daily check-ins (wellness, dating, productivity) |
37+
| `roxy.location` | 3 | City geocoding for birth chart coordinates |
38+
| `roxy.usage` | 1 | API usage stats and subscription info |
39+
<!-- END:DOMAINS -->
40+
41+
**Total:** 130 endpoints across 10 domains + usage. Counts auto-sync from `specs/openapi.json` at release time.
3742

3843
## Critical patterns
3944

@@ -83,6 +88,24 @@ const { data } = await roxy.crystals.searchCrystals({
8388
});
8489
```
8590

91+
### Multi-language responses via `query: { lang }`
92+
93+
Interpretations are available in 8 languages: `en`, `tr`, `de`, `es`, `fr`, `hi`, `pt`, `ru`. Pass `lang` as a query param on any supported endpoint. Defaults to `en`.
94+
95+
```typescript
96+
const { data } = await roxy.tarot.getDailyCard({
97+
body: { date: '2026-04-14' },
98+
query: { lang: 'es' },
99+
});
100+
101+
const { data } = await roxy.numerology.calculateLifePath({
102+
body: { year: 1990, month: 1, day: 15 },
103+
query: { lang: 'hi' },
104+
});
105+
```
106+
107+
Supported: `astrology`, `vedicAstrology`, `tarot`, `numerology`, `crystals`, `iching`, `angelNumbers`, `biorhythm`. Not supported (English-only): `dreams`, `location`, `usage`. Languages without translations yet fall back to English.
108+
86109
### Error handling
87110

88111
All errors return `{ error: string, code: string }`. The `error` field is human-readable (may change wording). The `code` field is machine-readable (stable, safe to switch on).
@@ -130,6 +153,9 @@ Error codes:
130153
| I Ching reading | `roxy.iching.castReading()` |
131154
| Angel number meaning | `roxy.angelNumbers.getAngelNumber({ path: { number: '1111' } })` |
132155
| Dream symbol lookup | `roxy.dreams.getDreamSymbol({ path: { id: 'flying' } })` |
156+
| Biorhythm reading | `roxy.biorhythm.getReading({ body: { birthDate: '1990-01-15' } })` |
157+
| Biorhythm forecast | `roxy.biorhythm.getForecast({ body: { birthDate: '1990-01-15', endDate: '2026-05-01' } })` |
158+
| Biorhythm compatibility | `roxy.biorhythm.calculateBioCompatibility({ body: { person1: { birthDate }, person2: { birthDate } } })` |
133159
| Find city coordinates | `roxy.location.searchCities({ query: { q: 'Mumbai' } })` |
134160
| Check API usage | `roxy.usage.getUsageStats()` |
135161

README.md

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,21 @@ const { data: reading } = await roxy.tarot.castCelticCross({
4949

5050
## Domains
5151

52-
| Namespace | What it covers |
53-
|-----------|----------------|
54-
| `roxy.angelNumbers` | Angel number lookup, pattern analysis, daily guidance |
55-
| `roxy.astrology` | Western astrology:natal charts, horoscopes, synastry, moon phases |
56-
| `roxy.vedicAstrology` | Vedic/Jyotish:birth charts, dashas, nakshatras, panchang, KP system |
57-
| `roxy.tarot` | 78-card readings:spreads, daily pulls, yes/no, Celtic Cross |
58-
| `roxy.numerology` | Life path, expression, soul urge, personal year, karmic lessons |
59-
| `roxy.iching` | I Ching hexagrams, trigrams, daily readings |
60-
| `roxy.crystals` | Crystal meanings, healing properties, zodiac and chakra pairings |
61-
| `roxy.dreams` | Dream symbol interpretations:3,000+ symbols |
62-
| `roxy.location` | City and country search for birth chart coordinates |
63-
| `roxy.usage` | API usage stats, rate limits, subscription info |
52+
<!-- BEGIN:DOMAINS -->
53+
| Namespace | Endpoints | What it covers |
54+
|-----------|-----------|----------------|
55+
| `roxy.astrology` | 22 | Western astrology: natal charts, horoscopes, synastry, moon phases |
56+
| `roxy.vedicAstrology` | 42 | Vedic/Jyotish: birth charts, dashas, nakshatras, panchang, KP system |
57+
| `roxy.tarot` | 10 | 78-card readings: spreads, daily pulls, yes/no, Celtic Cross |
58+
| `roxy.numerology` | 16 | Life path, expression, soul urge, personal year, karmic lessons |
59+
| `roxy.dreams` | 5 | Dream symbol dictionary: 3,000+ interpretations |
60+
| `roxy.angelNumbers` | 4 | Angel number lookup, pattern analysis, daily guidance |
61+
| `roxy.iching` | 9 | I Ching hexagrams, trigrams, daily readings |
62+
| `roxy.crystals` | 12 | Crystal meanings, healing properties, zodiac and chakra pairings |
63+
| `roxy.biorhythm` | 6 | 10-cycle biorhythm readings, forecasts, critical days, compatibility |
64+
| `roxy.location` | 3 | City and country search for birth chart coordinates |
65+
| `roxy.usage` | 1 | API usage stats, rate limits, subscription info |
66+
<!-- END:DOMAINS -->
6467

6568
## Authentication
6669

@@ -89,6 +92,19 @@ const client = createClient(
8992
const roxy = new Roxy({ client });
9093
```
9194

95+
## Multi-language responses
96+
97+
Interpretations, readings, and editorial text are available in 8 languages: English (`en`), Turkish (`tr`), German (`de`), Spanish (`es`), French (`fr`), Hindi (`hi`), Portuguese (`pt`), Russian (`ru`). Pass `query: { lang }` on any supported endpoint:
98+
99+
```typescript
100+
const { data } = await roxy.tarot.getDailyCard({
101+
body: { date: '2026-04-14' },
102+
query: { lang: 'es' },
103+
});
104+
```
105+
106+
Supported domains: `astrology`, `vedicAstrology`, `tarot`, `numerology`, `crystals`, `iching`, `angelNumbers`, `biorhythm`. `dreams`, `location`, and `usage` are English-only (dream content is pre-authored, and the other two are structural). Languages without translations yet fall back to English.
107+
92108
## Error handling
93109

94110
Every method returns `{ data, error, response }`. Errors have `{ error: string, code: string }` — switch on `code` for programmatic handling.

docs/llms-full.txt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ import { createRoxy } from '@roxyapi/sdk';
99
const roxy = createRoxy(process.env.ROXY_API_KEY!);
1010
```
1111

12+
**Multi-language responses.** Interpretations are available in 8 languages: `en`, `tr`, `de`, `es`, `fr`, `hi`, `pt`, `ru`. Pass `query: { lang }` on any supported endpoint. Supported: astrology, vedicAstrology, tarot, numerology, crystals, iching, angelNumbers, biorhythm. English-only: dreams, location, usage. Languages without translations yet fall back to English.
13+
14+
```typescript
15+
const { data } = await roxy.astrology.getDailyHoroscope({
16+
path: { sign: 'aries' },
17+
query: { lang: 'es' },
18+
});
19+
```
20+
1221
---
1322

1423
## Astrology (Western) — `roxy.astrology`
@@ -613,6 +622,48 @@ const { data } = await roxy.dreams.getDailyDreamSymbol({ body: { date: '2026-03-
613622

614623
---
615624

625+
## Biorhythm — `roxy.biorhythm`
626+
627+
10-cycle biorhythm calculations (physical, emotional, intellectual, intuitive, aesthetic, awareness, spiritual, passion, mastery, wisdom). All methods are POST. Only `birthDate` (`YYYY-MM-DD`) is required; dates default to today (UTC) when omitted. Every method accepts an optional `query: { lang }` (`en`, `tr`, `de`, `es`, `hi`, `pt`, `fr`, `ru`).
628+
629+
```typescript
630+
// Complete reading for a target date — all 10 cycles, phase detection, energy rating, interpretation, advice, critical day alerts
631+
const { data } = await roxy.biorhythm.getReading({
632+
body: { birthDate: '1990-01-15', targetDate: '2026-04-14' },
633+
});
634+
635+
// Multi-day forecast (max 90-day range). Returns daily cycle values, energy ratings, critical days, best/worst day summary
636+
const { data } = await roxy.biorhythm.getForecast({
637+
body: { birthDate: '1990-01-15', startDate: '2026-04-14', endDate: '2026-05-14' },
638+
});
639+
640+
// Critical days (zero crossings) in a range (max 180 days). Flags rare double and triple critical days
641+
const { data } = await roxy.biorhythm.getCriticalDays({
642+
body: { birthDate: '1990-01-15', startDate: '2026-04-14', endDate: '2026-07-14' },
643+
});
644+
645+
// Compatibility between two people on a target date (per-cycle scores, overall rating, strengths, challenges, advice)
646+
const { data } = await roxy.biorhythm.calculateBioCompatibility({
647+
body: {
648+
person1: { birthDate: '1990-01-15' },
649+
person2: { birthDate: '1988-06-22' },
650+
targetDate: '2026-04-14',
651+
},
652+
});
653+
654+
// Lightweight phase info for dashboards and widgets — no editorial text
655+
const { data } = await roxy.biorhythm.getPhases({
656+
body: { birthDate: '1990-01-15' },
657+
});
658+
659+
// Daily check-in with seeded randomness — same seed + same date = same reading (perfect for push notifications)
660+
const { data } = await roxy.biorhythm.getDailyBiorhythm({
661+
body: { birthDate: '1990-01-15', seed: 'user-123' },
662+
});
663+
```
664+
665+
---
666+
616667
## Location — `roxy.location`
617668

618669
Geocoding helper for birth chart coordinates.

lefthook.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ pre-commit:
77
stage_fixed: true
88
typecheck:
99
run: bun typecheck
10+
docs-sync:
11+
glob: "{specs/openapi.json,scripts/tag-descriptions.ts,scripts/sync-docs.ts,docs/llms-full.txt}"
12+
run: bun run docs:sync && git add README.md AGENTS.md
13+
stage_fixed: true
1014

1115
pre-push:
1216
parallel: false
@@ -17,6 +21,9 @@ pre-push:
1721
biome-full:
1822
run: bun run check
1923
priority: 2
24+
docs-drift:
25+
run: bun run docs:sync && git diff --exit-code -- README.md AGENTS.md || { echo "✗ README.md/AGENTS.md drifted from spec — commit the sync output"; exit 1; }
26+
priority: 3
2027
test:
2128
run: bun run test
22-
priority: 3
29+
priority: 4

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
],
2727
"scripts": {
2828
"generate": "bun run scripts/generate.ts",
29+
"docs:sync": "bun run scripts/sync-docs.ts",
2930
"build": "tsup src/factory.ts src/client/index.ts --format esm,cjs && tsc -p tsconfig.build.json --emitDeclarationOnly",
3031
"typecheck": "tsc --noEmit",
3132
"test": "vitest run",

scripts/generate.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ console.log('Spec saved to', SPEC_PATH);
2828
console.log('Running hey-api generator...');
2929
execSync('bunx openapi-ts', { stdio: 'inherit' });
3030
console.log('SDK generated successfully.');
31+
32+
console.log('Syncing README.md and AGENTS.md from spec...');
33+
execSync('bun run scripts/sync-docs.ts', { stdio: 'inherit' });

scripts/sync-docs.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env bun
2+
/**
3+
* Regenerate the domain tables in README.md and AGENTS.md from
4+
* `specs/openapi.json`. Also verifies every spec tag is mentioned
5+
* somewhere in `docs/llms-full.txt`.
6+
*
7+
* Run with: bun run docs:sync
8+
*
9+
* Fails loudly if:
10+
* - A new OpenAPI tag lands that has no entry in `scripts/tag-descriptions.ts`.
11+
* - An entry in `scripts/tag-descriptions.ts` refers to a tag that no
12+
* longer exists in the spec.
13+
* - A tag is missing a section in `docs/llms-full.txt`.
14+
* - README.md or AGENTS.md is missing the `<!-- BEGIN:DOMAINS -->` markers.
15+
*/
16+
import { readFileSync, writeFileSync } from 'node:fs';
17+
import { tagDescriptions } from './tag-descriptions';
18+
19+
type OpenApiSpec = {
20+
tags?: Array<{ name: string }>;
21+
paths?: Record<string, Record<string, { tags?: string[] }>>;
22+
};
23+
24+
const SPEC_PATH = 'specs/openapi.json';
25+
const LLMS_PATH = 'docs/llms-full.txt';
26+
const README_PATH = 'README.md';
27+
const AGENTS_PATH = 'AGENTS.md';
28+
const BEGIN = '<!-- BEGIN:DOMAINS -->';
29+
const END = '<!-- END:DOMAINS -->';
30+
31+
function fail(msg: string): never {
32+
console.error(`\n✗ sync-docs: ${msg}\n`);
33+
process.exit(1);
34+
}
35+
36+
const spec = JSON.parse(readFileSync(SPEC_PATH, 'utf-8')) as OpenApiSpec;
37+
38+
const specTags = (spec.tags ?? []).map((t) => t.name);
39+
if (specTags.length === 0)
40+
fail(`${SPEC_PATH} has no .tags[] — spec is malformed?`);
41+
42+
const unknown = specTags.filter((t) => !(t in tagDescriptions));
43+
if (unknown.length > 0) {
44+
fail(
45+
`Unknown tag(s) in spec: ${unknown.map((t) => `"${t}"`).join(', ')}. Add entries to scripts/tag-descriptions.ts before releasing.`,
46+
);
47+
}
48+
49+
const stale = Object.keys(tagDescriptions).filter((t) => !specTags.includes(t));
50+
if (stale.length > 0) {
51+
fail(
52+
`Stale tag(s) in scripts/tag-descriptions.ts (not in spec): ${stale.map((t) => `"${t}"`).join(', ')}. Remove them.`,
53+
);
54+
}
55+
56+
const endpointCounts = new Map<string, number>();
57+
for (const methods of Object.values(spec.paths ?? {})) {
58+
for (const op of Object.values(methods)) {
59+
const tag = op.tags?.[0];
60+
if (tag) endpointCounts.set(tag, (endpointCounts.get(tag) ?? 0) + 1);
61+
}
62+
}
63+
64+
const llms = readFileSync(LLMS_PATH, 'utf-8');
65+
const missingInLlms = specTags.filter((t) => {
66+
const entry = tagDescriptions[t];
67+
return entry && !llms.includes(entry.namespace);
68+
});
69+
if (missingInLlms.length > 0) {
70+
fail(
71+
`Tag(s) missing from ${LLMS_PATH}: ${missingInLlms.map((t) => `"${t}"`).join(', ')}. Add a section for each before releasing.`,
72+
);
73+
}
74+
75+
function renderTable(kind: 'readme' | 'agent'): string {
76+
const rows = specTags.map((tag) => {
77+
const entry = tagDescriptions[tag];
78+
if (!entry) throw new Error(`unreachable: tag ${tag} missing after check`);
79+
const count = endpointCounts.get(tag) ?? 0;
80+
const desc =
81+
kind === 'readme' ? entry.readmeDescription : entry.agentDescription;
82+
return `| \`${entry.namespace}\` | ${count} | ${desc} |`;
83+
});
84+
return [
85+
BEGIN,
86+
'| Namespace | Endpoints | What it covers |',
87+
'|-----------|-----------|----------------|',
88+
...rows,
89+
END,
90+
].join('\n');
91+
}
92+
93+
function syncFile(path: string, kind: 'readme' | 'agent'): boolean {
94+
const src = readFileSync(path, 'utf-8');
95+
const beginIdx = src.indexOf(BEGIN);
96+
const endIdx = src.indexOf(END);
97+
if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {
98+
fail(`${path} is missing ${BEGIN} / ${END} markers`);
99+
}
100+
const before = src.slice(0, beginIdx);
101+
const after = src.slice(endIdx + END.length);
102+
const next = `${before}${renderTable(kind)}${after}`;
103+
if (next === src) return false;
104+
writeFileSync(path, next);
105+
return true;
106+
}
107+
108+
const readmeChanged = syncFile(README_PATH, 'readme');
109+
const agentsChanged = syncFile(AGENTS_PATH, 'agent');
110+
111+
const totalEndpoints = [...endpointCounts.values()].reduce((a, b) => a + b, 0);
112+
console.log(
113+
`✓ sync-docs: ${specTags.length} tags, ${totalEndpoints} endpoints. ` +
114+
`README ${readmeChanged ? 'updated' : 'unchanged'}, ` +
115+
`AGENTS ${agentsChanged ? 'updated' : 'unchanged'}.`,
116+
);

0 commit comments

Comments
 (0)