ether-to-astro 1.3.0 → 1.4.0
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 +5 -1
- package/dist/astro-service/natal-service.d.ts +6 -2
- package/dist/astro-service/natal-service.js +43 -4
- package/dist/astro-service/service-types.d.ts +18 -1
- package/dist/astro-service/shared.d.ts +8 -1
- package/dist/astro-service/shared.js +15 -1
- package/dist/astro-service/sign-boundary-service.d.ts +36 -0
- package/dist/astro-service/sign-boundary-service.js +156 -0
- package/dist/astro-service/sky-service.d.ts +1 -4
- package/dist/astro-service/sky-service.js +18 -14
- package/dist/astro-service/transit-service.js +7 -5
- package/dist/astro-service.d.ts +14 -3
- package/dist/astro-service.js +80 -10
- package/dist/cli.js +23 -0
- package/dist/entrypoint.d.ts +1 -0
- package/dist/entrypoint.js +13 -0
- package/dist/loader.js +2 -2
- package/dist/mcp-alias.js +2 -1
- package/dist/tool-registry.d.ts +1 -1
- package/dist/tool-registry.js +111 -8
- package/dist/tool-result.js +2 -1
- package/dist/types.d.ts +25 -2
- package/dist/types.js +45 -0
- package/docs/releases/1.4.0.md +36 -0
- package/docs/releases/README.md +3 -2
- package/package.json +1 -1
- package/skills/.curated/daily-brief/SKILL.md +10 -6
- package/skills/.curated/weekly-overview/SKILL.md +9 -6
- package/src/astro-service/natal-service.ts +57 -4
- package/src/astro-service/service-types.ts +20 -1
- package/src/astro-service/shared.ts +23 -1
- package/src/astro-service/sign-boundary-service.ts +222 -0
- package/src/astro-service/sky-service.ts +21 -16
- package/src/astro-service/transit-service.ts +7 -4
- package/src/astro-service.ts +108 -11
- package/src/cli.ts +46 -0
- package/src/entrypoint.ts +17 -0
- package/src/loader.ts +2 -2
- package/src/mcp-alias.ts +2 -1
- package/src/tool-registry.ts +129 -9
- package/src/tool-result.ts +2 -1
- package/src/types.ts +72 -18
- package/tests/property/transits.property.test.ts +3 -13
- package/tests/unit/astro-service/natal-service.test.ts +16 -2
- package/tests/unit/astro-service/sign-boundary-service.test.ts +188 -0
- package/tests/unit/astro-service/sky-service.test.ts +8 -6
- package/tests/unit/astro-service/transit-service.test.ts +41 -0
- package/tests/unit/astro-service.test.ts +161 -8
- package/tests/unit/cli-commands.test.ts +1 -0
- package/tests/unit/entrypoint.test.ts +101 -2
- package/tests/unit/error-mapping.test.ts +7 -0
- package/tests/unit/tool-registry.test.ts +43 -1
- package/tests/validation/adapters/astrolog.ts +2 -14
package/dist/astro-service.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ElectionalService } from './astro-service/electional-service.js';
|
|
|
4
4
|
import { NatalService } from './astro-service/natal-service.js';
|
|
5
5
|
import { RisingSignService } from './astro-service/rising-sign-service.js';
|
|
6
6
|
import { resolveReportingTimezone } from './astro-service/shared.js';
|
|
7
|
+
import { SignBoundaryService } from './astro-service/sign-boundary-service.js';
|
|
7
8
|
import { SkyService } from './astro-service/sky-service.js';
|
|
8
9
|
import { TransitService } from './astro-service/transit-service.js';
|
|
9
10
|
import { ChartRenderer } from './charts.js';
|
|
@@ -12,7 +13,9 @@ import { EphemerisCalculator } from './ephemeris.js';
|
|
|
12
13
|
import { formatInTimezone } from './formatter.js';
|
|
13
14
|
import { HouseCalculator } from './houses.js';
|
|
14
15
|
import { RiseSetCalculator } from './riseset.js';
|
|
16
|
+
import { isValidTimezone } from './time-utils.js';
|
|
15
17
|
import { TransitCalculator } from './transits.js';
|
|
18
|
+
const VALID_RUNTIME_HOUSE_STYLES = new Set(['P', 'W', 'K', 'E']);
|
|
16
19
|
export { parseDateOnlyInput } from './astro-service/date-input.js';
|
|
17
20
|
/**
|
|
18
21
|
* Shared service facade used by both the MCP server and the CLI.
|
|
@@ -29,9 +32,11 @@ export class AstroService {
|
|
|
29
32
|
eclipseCalc;
|
|
30
33
|
chartRenderer;
|
|
31
34
|
mcpStartupDefaults;
|
|
35
|
+
runtimePreferences = {};
|
|
32
36
|
transitService;
|
|
33
37
|
electionalService;
|
|
34
38
|
risingSignService;
|
|
39
|
+
signBoundaryService;
|
|
35
40
|
natalService;
|
|
36
41
|
skyService;
|
|
37
42
|
chartOutputService;
|
|
@@ -63,6 +68,10 @@ export class AstroService {
|
|
|
63
68
|
ephem: this.ephem,
|
|
64
69
|
houseCalc: this.houseCalc,
|
|
65
70
|
});
|
|
71
|
+
this.signBoundaryService = new SignBoundaryService({
|
|
72
|
+
ephem: this.ephem,
|
|
73
|
+
now: this.now,
|
|
74
|
+
});
|
|
66
75
|
this.natalService = new NatalService({
|
|
67
76
|
ephem: this.ephem,
|
|
68
77
|
houseCalc: this.houseCalc,
|
|
@@ -73,7 +82,6 @@ export class AstroService {
|
|
|
73
82
|
ephem: this.ephem,
|
|
74
83
|
riseSetCalc: this.riseSetCalc,
|
|
75
84
|
eclipseCalc: this.eclipseCalc,
|
|
76
|
-
mcpStartupDefaults: this.mcpStartupDefaults,
|
|
77
85
|
now: this.now,
|
|
78
86
|
formatTimestamp: this.formatTimestamp.bind(this),
|
|
79
87
|
});
|
|
@@ -99,7 +107,18 @@ export class AstroService {
|
|
|
99
107
|
* timezone, and finally UTC.
|
|
100
108
|
*/
|
|
101
109
|
resolveReportingTimezone(explicitTimezone, natalTimezone) {
|
|
102
|
-
return
|
|
110
|
+
return (explicitTimezone ??
|
|
111
|
+
this.runtimePreferences.preferredTimezone ??
|
|
112
|
+
resolveReportingTimezone(this.mcpStartupDefaults, undefined, natalTimezone));
|
|
113
|
+
}
|
|
114
|
+
applyRuntimeHouseStyle(natalChart) {
|
|
115
|
+
if (this.runtimePreferences.preferredHouseStyle === undefined) {
|
|
116
|
+
return natalChart;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...natalChart,
|
|
120
|
+
requestedHouseSystem: this.runtimePreferences.preferredHouseStyle,
|
|
121
|
+
};
|
|
103
122
|
}
|
|
104
123
|
/**
|
|
105
124
|
* Initialize the underlying ephemeris engine.
|
|
@@ -131,7 +150,10 @@ export class AstroService {
|
|
|
131
150
|
* may use a different reporting timezone for labels when startup defaults are set.
|
|
132
151
|
*/
|
|
133
152
|
getTransits(natalChart, input = {}) {
|
|
134
|
-
|
|
153
|
+
const effectiveInput = input.timezone === undefined && this.runtimePreferences.preferredTimezone !== undefined
|
|
154
|
+
? { ...input, timezone: this.runtimePreferences.preferredTimezone }
|
|
155
|
+
: input;
|
|
156
|
+
return this.transitService.getTransits(this.applyRuntimeHouseStyle(natalChart), effectiveInput);
|
|
135
157
|
}
|
|
136
158
|
/**
|
|
137
159
|
* Produce deterministic electional context for a single local instant.
|
|
@@ -151,7 +173,7 @@ export class AstroService {
|
|
|
151
173
|
* chart preference, then startup defaults.
|
|
152
174
|
*/
|
|
153
175
|
getHouses(natalChart, input = {}) {
|
|
154
|
-
return this.natalService.getHouses(natalChart, input);
|
|
176
|
+
return this.natalService.getHouses(this.applyRuntimeHouseStyle(natalChart), input);
|
|
155
177
|
}
|
|
156
178
|
/**
|
|
157
179
|
* Find rising-sign windows across a calendar day at a specific location.
|
|
@@ -163,11 +185,21 @@ export class AstroService {
|
|
|
163
185
|
getRisingSignWindows(input) {
|
|
164
186
|
return this.risingSignService.getRisingSignWindows(input);
|
|
165
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Return exact sign-boundary events across a local calendar window.
|
|
190
|
+
*/
|
|
191
|
+
getSignBoundaryEvents(input = {}) {
|
|
192
|
+
const timezone = this.resolveReportingTimezone(input.timezone);
|
|
193
|
+
return this.signBoundaryService.getSignBoundaryEvents({
|
|
194
|
+
...input,
|
|
195
|
+
timezone,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
166
198
|
/**
|
|
167
199
|
* Return the currently retrograde planets for the requested reporting timezone.
|
|
168
200
|
*/
|
|
169
201
|
getRetrogradePlanets(timezone) {
|
|
170
|
-
return this.skyService.getRetrogradePlanets(timezone);
|
|
202
|
+
return this.skyService.getRetrogradePlanets(this.resolveReportingTimezone(timezone));
|
|
171
203
|
}
|
|
172
204
|
/**
|
|
173
205
|
* Return the next rise and set events after the local day anchor for the chart location.
|
|
@@ -176,26 +208,64 @@ export class AstroService {
|
|
|
176
208
|
* The lookup anchor remains local midnight in the natal chart timezone even
|
|
177
209
|
* when reporting text uses a preferred reporting timezone.
|
|
178
210
|
*/
|
|
179
|
-
async getRiseSetTimes(natalChart) {
|
|
180
|
-
return this.skyService.getRiseSetTimes(natalChart);
|
|
211
|
+
async getRiseSetTimes(natalChart, timezone) {
|
|
212
|
+
return this.skyService.getRiseSetTimes(natalChart, this.resolveReportingTimezone(timezone, natalChart.location.timezone));
|
|
181
213
|
}
|
|
182
214
|
/**
|
|
183
215
|
* Return current asteroid and node positions for the requested reporting timezone.
|
|
184
216
|
*/
|
|
185
217
|
getAsteroidPositions(timezone) {
|
|
186
|
-
return this.skyService.getAsteroidPositions(timezone);
|
|
218
|
+
return this.skyService.getAsteroidPositions(this.resolveReportingTimezone(timezone));
|
|
187
219
|
}
|
|
188
220
|
/**
|
|
189
221
|
* Look up the next solar and lunar eclipses after the current instant.
|
|
190
222
|
*/
|
|
191
223
|
getNextEclipses(timezone) {
|
|
192
|
-
return this.skyService.getNextEclipses(timezone);
|
|
224
|
+
return this.skyService.getNextEclipses(this.resolveReportingTimezone(timezone));
|
|
193
225
|
}
|
|
194
226
|
/**
|
|
195
227
|
* Summarize process-local server state and configured startup defaults.
|
|
196
228
|
*/
|
|
197
229
|
getServerStatus(natalChart) {
|
|
198
|
-
return this.natalService.getServerStatus(natalChart);
|
|
230
|
+
return this.natalService.getServerStatus(natalChart, this.runtimePreferences);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Update process-local MCP runtime preferences.
|
|
234
|
+
*/
|
|
235
|
+
setPreferences(input) {
|
|
236
|
+
if (input.preferred_timezone !== undefined) {
|
|
237
|
+
if (input.preferred_timezone === null) {
|
|
238
|
+
delete this.runtimePreferences.preferredTimezone;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
if (!isValidTimezone(input.preferred_timezone)) {
|
|
242
|
+
throw new Error(`Invalid timezone: ${input.preferred_timezone}`);
|
|
243
|
+
}
|
|
244
|
+
this.runtimePreferences.preferredTimezone = input.preferred_timezone;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (input.preferred_house_style !== undefined) {
|
|
248
|
+
if (input.preferred_house_style === null) {
|
|
249
|
+
delete this.runtimePreferences.preferredHouseStyle;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
if (!VALID_RUNTIME_HOUSE_STYLES.has(input.preferred_house_style)) {
|
|
253
|
+
throw new Error(`Invalid preferred house style: ${input.preferred_house_style} (must be one of P, W, K, E)`);
|
|
254
|
+
}
|
|
255
|
+
this.runtimePreferences.preferredHouseStyle = input.preferred_house_style;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const preferredTimezone = this.runtimePreferences.preferredTimezone ?? null;
|
|
259
|
+
const preferredHouseStyle = this.runtimePreferences.preferredHouseStyle ?? null;
|
|
260
|
+
return {
|
|
261
|
+
data: {
|
|
262
|
+
runtimePreferences: {
|
|
263
|
+
preferredTimezone,
|
|
264
|
+
preferredHouseStyle,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
text: `Runtime preferences updated. Reporting timezone: ${preferredTimezone ?? 'default'}. House style: ${preferredHouseStyle ?? 'default'}.`,
|
|
268
|
+
};
|
|
199
269
|
}
|
|
200
270
|
/**
|
|
201
271
|
* Generate a natal chart image or SVG for the current chart.
|
package/dist/cli.js
CHANGED
|
@@ -308,6 +308,29 @@ export async function runCli(argv, io = {
|
|
|
308
308
|
const result = await spec.execute({ service, natalChart: null }, { timezone });
|
|
309
309
|
emitExecution(io, result, options.pretty ?? false);
|
|
310
310
|
});
|
|
311
|
+
program
|
|
312
|
+
.command('get-sign-boundary-events')
|
|
313
|
+
.description(mustTool('get_sign_boundary_events').description)
|
|
314
|
+
.option('--date <yyyy-mm-dd>', toolSchemaProperty('get_sign_boundary_events', 'date').description ?? 'Start local date')
|
|
315
|
+
.option('--days-ahead <number>', toolSchemaProperty('get_sign_boundary_events', 'days_ahead').description ?? 'Days ahead')
|
|
316
|
+
.option('--timezone <tz>', toolSchemaProperty('get_sign_boundary_events', 'timezone').description ?? 'Timezone')
|
|
317
|
+
.option('--bodies <list>', 'Comma-separated list of bodies to scan (Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto)')
|
|
318
|
+
.option('--pretty', 'Human-readable output instead of JSON')
|
|
319
|
+
.action(async (options) => {
|
|
320
|
+
const spec = mustTool('get_sign_boundary_events');
|
|
321
|
+
const timezone = await resolveCommandTimezone(options);
|
|
322
|
+
const bodies = options.bodies
|
|
323
|
+
?.split(',')
|
|
324
|
+
.map((value) => value.trim())
|
|
325
|
+
.filter(Boolean);
|
|
326
|
+
const result = await spec.execute({ service, natalChart: null }, {
|
|
327
|
+
date: options.date,
|
|
328
|
+
timezone,
|
|
329
|
+
days_ahead: options.daysAhead == null ? undefined : toNumber(options.daysAhead, 'days-ahead'),
|
|
330
|
+
bodies,
|
|
331
|
+
});
|
|
332
|
+
emitExecution(io, result, options.pretty ?? false);
|
|
333
|
+
});
|
|
311
334
|
program
|
|
312
335
|
.command('get-electional-context')
|
|
313
336
|
.description(mustTool('get_electional_context').description)
|
package/dist/entrypoint.d.ts
CHANGED
|
@@ -11,3 +11,4 @@ export interface EntrypointResolution {
|
|
|
11
11
|
mcpStartupDefaults: Readonly<McpStartupDefaults>;
|
|
12
12
|
}
|
|
13
13
|
export declare function resolveEntrypoint(argv: string[], invokedPath?: string): EntrypointResolution;
|
|
14
|
+
export declare function isDirectExecution(importMetaUrl: string, invokedPath?: string): boolean;
|
package/dist/entrypoint.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { isValidTimezone } from './time-utils.js';
|
|
3
5
|
const VALID_HOUSE_STYLES = new Set(['P', 'W', 'K', 'E']);
|
|
4
6
|
function readOptionValue(argv, index, flag) {
|
|
@@ -76,3 +78,14 @@ export function resolveEntrypoint(argv, invokedPath = process.argv[1] ?? '') {
|
|
|
76
78
|
mcpStartupDefaults: Object.freeze({ ...defaults }),
|
|
77
79
|
};
|
|
78
80
|
}
|
|
81
|
+
export function isDirectExecution(importMetaUrl, invokedPath = process.argv[1] ?? '') {
|
|
82
|
+
if (!invokedPath) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(invokedPath);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/dist/loader.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
// Set up browser globals for astrochart library BEFORE any imports
|
|
5
5
|
import { JSDOM } from 'jsdom';
|
|
6
|
-
import { resolveEntrypoint } from './entrypoint.js';
|
|
6
|
+
import { isDirectExecution, resolveEntrypoint } from './entrypoint.js';
|
|
7
7
|
function initializeRuntime() {
|
|
8
8
|
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
9
9
|
globalThis.window = dom.window;
|
|
@@ -61,7 +61,7 @@ export async function runEntrypoint(argv = process.argv.slice(2), invokedPath =
|
|
|
61
61
|
const code = await runCli(resolved.cliArgv);
|
|
62
62
|
process.exit(code);
|
|
63
63
|
}
|
|
64
|
-
if (import.meta.url
|
|
64
|
+
if (isDirectExecution(import.meta.url)) {
|
|
65
65
|
runEntrypoint().catch((error) => {
|
|
66
66
|
console.error('[ERROR] Failed to start program:', error);
|
|
67
67
|
process.exit(1);
|
package/dist/mcp-alias.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { isDirectExecution } from './entrypoint.js';
|
|
2
3
|
import { runEntrypoint } from './loader.js';
|
|
3
|
-
if (import.meta.url
|
|
4
|
+
if (isDirectExecution(import.meta.url)) {
|
|
4
5
|
runEntrypoint(['--mcp', ...process.argv.slice(2)], process.argv[1]).catch((error) => {
|
|
5
6
|
console.error('[ERROR] Failed to start program:', error);
|
|
6
7
|
process.exit(1);
|
package/dist/tool-registry.d.ts
CHANGED
package/dist/tool-registry.js
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
|
+
import { SIGN_BOUNDARY_BODIES, } from './types.js';
|
|
1
2
|
export const MCP_TOOL_SPECS = [
|
|
3
|
+
{
|
|
4
|
+
name: 'set_preferences',
|
|
5
|
+
description: 'Update process-local MCP runtime preferences. Use this to change session reporting defaults such as timezone and house style without restarting the server.',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
preferred_timezone: {
|
|
10
|
+
anyOf: [{ type: 'string' }, { type: 'null' }],
|
|
11
|
+
description: 'Optional reporting timezone override for this MCP session. Use null to clear the override.',
|
|
12
|
+
},
|
|
13
|
+
preferred_house_style: {
|
|
14
|
+
anyOf: [{ type: 'string', enum: ['P', 'W', 'K', 'E'] }, { type: 'null' }],
|
|
15
|
+
description: 'Optional preferred house style override for this MCP session. Use null to clear the override.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
requiresNatalChart: false,
|
|
20
|
+
execute: (ctx, args) => {
|
|
21
|
+
const result = ctx.service.setPreferences({
|
|
22
|
+
preferred_timezone: args.preferred_timezone,
|
|
23
|
+
preferred_house_style: args.preferred_house_style,
|
|
24
|
+
});
|
|
25
|
+
return { kind: 'state', data: result.data, text: result.text };
|
|
26
|
+
},
|
|
27
|
+
},
|
|
2
28
|
{
|
|
3
29
|
name: 'set_natal_chart',
|
|
4
30
|
description: 'Store natal chart data for transit calculations. Birth time should be LOCAL time at the birth location (not UTC). The server converts to UTC using the timezone parameter. Optional birth_time_disambiguation handles DST overlap/gap edge cases (default: reject).',
|
|
@@ -98,6 +124,46 @@ export const MCP_TOOL_SPECS = [
|
|
|
98
124
|
return { kind: 'state', data: result.data, text: result.text };
|
|
99
125
|
},
|
|
100
126
|
},
|
|
127
|
+
{
|
|
128
|
+
name: 'get_sign_boundary_events',
|
|
129
|
+
description: 'Return exact sign-boundary events for one or more planets across a local date window. Each event includes both from_sign and to_sign so ingress and egress are represented as one crossing.',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
date: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'Start local date (YYYY-MM-DD). Defaults to today in the resolved timezone.',
|
|
136
|
+
},
|
|
137
|
+
timezone: {
|
|
138
|
+
type: 'string',
|
|
139
|
+
description: 'Optional timezone used for local-day interpretation and reporting. Falls back to the current MCP session timezone.',
|
|
140
|
+
},
|
|
141
|
+
days_ahead: {
|
|
142
|
+
type: 'number',
|
|
143
|
+
description: 'Number of days to look ahead from the start date. Defaults to 0 for a single-day window.',
|
|
144
|
+
default: 0,
|
|
145
|
+
},
|
|
146
|
+
bodies: {
|
|
147
|
+
type: 'array',
|
|
148
|
+
items: {
|
|
149
|
+
type: 'string',
|
|
150
|
+
enum: [...SIGN_BOUNDARY_BODIES],
|
|
151
|
+
},
|
|
152
|
+
description: 'Optional list of supported bodies to scan. Defaults to all supported sign-boundary bodies.',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
requiresNatalChart: false,
|
|
157
|
+
execute: (ctx, args) => {
|
|
158
|
+
const result = ctx.service.getSignBoundaryEvents({
|
|
159
|
+
date: args.date,
|
|
160
|
+
timezone: args.timezone,
|
|
161
|
+
days_ahead: args.days_ahead,
|
|
162
|
+
bodies: args.bodies,
|
|
163
|
+
});
|
|
164
|
+
return { kind: 'state', data: result.data, text: result.text };
|
|
165
|
+
},
|
|
166
|
+
},
|
|
101
167
|
{
|
|
102
168
|
name: 'get_electional_context',
|
|
103
169
|
description: 'Get stateless electional context for a specific local date, time, and location. Returns deterministic timing facts such as ascendant, sect/day-night classification, Moon phase, applying aspects, and optional ascendant-ruler basics. This tool does not require a natal chart and is separate from get_transits.',
|
|
@@ -168,6 +234,10 @@ export const MCP_TOOL_SPECS = [
|
|
|
168
234
|
type: 'string',
|
|
169
235
|
description: 'Date for transits (ISO format YYYY-MM-DD). Defaults to today.',
|
|
170
236
|
},
|
|
237
|
+
timezone: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'Optional reporting timezone override. Calculation day interpretation still uses the natal chart timezone.',
|
|
240
|
+
},
|
|
171
241
|
categories: {
|
|
172
242
|
type: 'array',
|
|
173
243
|
items: { type: 'string', enum: ['moon', 'personal', 'outer', 'all'] },
|
|
@@ -175,7 +245,7 @@ export const MCP_TOOL_SPECS = [
|
|
|
175
245
|
},
|
|
176
246
|
include_mundane: {
|
|
177
247
|
type: 'boolean',
|
|
178
|
-
description: 'Include deterministic mundane baseline data for the requested window. Output includes planetary positions, transit-to-transit mundane aspects, and non-narrative weather grouping metadata; forecast windows also include per-day mundane.days entries. Defaults to false.',
|
|
248
|
+
description: 'Include deterministic mundane baseline data for the requested window. Output includes planetary positions using the same sign-boundary normalization as serialized transits, transit-to-transit mundane aspects, and non-narrative weather grouping metadata; forecast windows also include per-day mundane.days entries. Defaults to false.',
|
|
179
249
|
},
|
|
180
250
|
days_ahead: {
|
|
181
251
|
type: 'number',
|
|
@@ -206,6 +276,7 @@ export const MCP_TOOL_SPECS = [
|
|
|
206
276
|
execute: (ctx, args) => {
|
|
207
277
|
const result = ctx.service.getTransits(ctx.natalChart, {
|
|
208
278
|
date: args.date,
|
|
279
|
+
timezone: args.timezone,
|
|
209
280
|
categories: args.categories,
|
|
210
281
|
include_mundane: args.include_mundane,
|
|
211
282
|
days_ahead: args.days_ahead,
|
|
@@ -242,7 +313,15 @@ export const MCP_TOOL_SPECS = [
|
|
|
242
313
|
{
|
|
243
314
|
name: 'get_retrograde_planets',
|
|
244
315
|
description: 'Show which planets are currently retrograde',
|
|
245
|
-
inputSchema: {
|
|
316
|
+
inputSchema: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
timezone: {
|
|
320
|
+
type: 'string',
|
|
321
|
+
description: 'Optional reporting timezone override',
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
246
325
|
requiresNatalChart: false,
|
|
247
326
|
execute: (ctx, args) => {
|
|
248
327
|
const timezone = ctx.service.resolveReportingTimezone(args.timezone, ctx.natalChart?.location?.timezone);
|
|
@@ -253,17 +332,33 @@ export const MCP_TOOL_SPECS = [
|
|
|
253
332
|
{
|
|
254
333
|
name: 'get_rise_set_times',
|
|
255
334
|
description: 'Get sunrise, sunset, moonrise, moonset times for today',
|
|
256
|
-
inputSchema: {
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: 'object',
|
|
337
|
+
properties: {
|
|
338
|
+
timezone: {
|
|
339
|
+
type: 'string',
|
|
340
|
+
description: 'Optional reporting timezone override. Rise/set anchoring still uses the natal chart timezone.',
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
257
344
|
requiresNatalChart: true,
|
|
258
|
-
execute: async (ctx) => {
|
|
259
|
-
const result = await ctx.service.getRiseSetTimes(ctx.natalChart);
|
|
345
|
+
execute: async (ctx, args) => {
|
|
346
|
+
const result = await ctx.service.getRiseSetTimes(ctx.natalChart, args.timezone);
|
|
260
347
|
return { kind: 'state', data: result.data, text: result.text };
|
|
261
348
|
},
|
|
262
349
|
},
|
|
263
350
|
{
|
|
264
351
|
name: 'get_asteroid_positions',
|
|
265
352
|
description: 'Get positions of major asteroids (Chiron, Ceres, Pallas, Juno, Vesta) and Nodes',
|
|
266
|
-
inputSchema: {
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: 'object',
|
|
355
|
+
properties: {
|
|
356
|
+
timezone: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
description: 'Optional reporting timezone override',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
267
362
|
requiresNatalChart: false,
|
|
268
363
|
execute: (ctx, args) => {
|
|
269
364
|
const timezone = ctx.service.resolveReportingTimezone(args.timezone, ctx.natalChart?.location?.timezone);
|
|
@@ -274,7 +369,15 @@ export const MCP_TOOL_SPECS = [
|
|
|
274
369
|
{
|
|
275
370
|
name: 'get_next_eclipses',
|
|
276
371
|
description: 'Find the next solar and lunar eclipses',
|
|
277
|
-
inputSchema: {
|
|
372
|
+
inputSchema: {
|
|
373
|
+
type: 'object',
|
|
374
|
+
properties: {
|
|
375
|
+
timezone: {
|
|
376
|
+
type: 'string',
|
|
377
|
+
description: 'Optional reporting timezone override',
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
},
|
|
278
381
|
requiresNatalChart: false,
|
|
279
382
|
execute: (ctx, args) => {
|
|
280
383
|
const timezone = ctx.service.resolveReportingTimezone(args.timezone, ctx.natalChart?.location?.timezone);
|
|
@@ -284,7 +387,7 @@ export const MCP_TOOL_SPECS = [
|
|
|
284
387
|
},
|
|
285
388
|
{
|
|
286
389
|
name: 'get_server_status',
|
|
287
|
-
description: 'Inspect the current server state: whether a natal chart is loaded, its name and timezone, and the server version. Call this before making assumptions about loaded context.',
|
|
390
|
+
description: 'Inspect the current server state: whether a natal chart is loaded, its name and timezone, the effective reporting timezone and house-style context, and the server version. Call this before making assumptions about loaded context.',
|
|
288
391
|
inputSchema: { type: 'object', properties: {} },
|
|
289
392
|
requiresNatalChart: false,
|
|
290
393
|
execute: (ctx) => {
|
package/dist/tool-result.js
CHANGED
|
@@ -124,7 +124,8 @@ export function mapToolErrorMessageToCode(errorMessage) {
|
|
|
124
124
|
errorMessage.includes('missing julianDay') ||
|
|
125
125
|
errorMessage.includes('Invalid mode') ||
|
|
126
126
|
errorMessage.includes('Invalid latitude') ||
|
|
127
|
-
errorMessage.includes('Invalid longitude')
|
|
127
|
+
errorMessage.includes('Invalid longitude') ||
|
|
128
|
+
errorMessage.includes('Invalid preferred house style')) {
|
|
128
129
|
return 'INVALID_INPUT';
|
|
129
130
|
}
|
|
130
131
|
if (errorMessage.includes('Invalid timezone') || errorMessage.includes('timezone')) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export type HouseSystem = 'P' | 'K' | 'W' | 'E' | 'O' | 'R' | 'C' | 'A' | 'V' | 'X' | 'H' | 'T' | 'B';
|
|
2
2
|
export type SolarEclipseType = 'partial' | 'annular' | 'total' | 'annular-total';
|
|
3
3
|
export type LunarEclipseType = 'penumbral' | 'partial' | 'total';
|
|
4
|
-
export
|
|
4
|
+
export declare const CLASSICAL_PLANET_NAMES: readonly ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn"];
|
|
5
|
+
export declare const MAJOR_PLANET_NAMES: readonly ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"];
|
|
6
|
+
export declare const SUPPLEMENTAL_PLANET_NAMES: readonly ["Chiron", "North Node (Mean)", "North Node (True)", "Ceres", "Pallas", "Juno", "Vesta"];
|
|
7
|
+
export declare const ALL_PLANET_NAMES: readonly ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "Chiron", "North Node (Mean)", "North Node (True)", "Ceres", "Pallas", "Juno", "Vesta"];
|
|
8
|
+
export type PlanetName = (typeof ALL_PLANET_NAMES)[number];
|
|
9
|
+
export declare const SIGN_BOUNDARY_BODIES: readonly ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"];
|
|
10
|
+
export type SignBoundaryBody = (typeof SIGN_BOUNDARY_BODIES)[number];
|
|
5
11
|
/**
|
|
6
12
|
* Represents a complete natal birth chart with all necessary data for calculations
|
|
7
13
|
*
|
|
@@ -124,6 +130,22 @@ export interface PlanetPosition {
|
|
|
124
130
|
*/
|
|
125
131
|
isRetrograde: boolean;
|
|
126
132
|
}
|
|
133
|
+
export interface SignBoundaryEvent {
|
|
134
|
+
body: SignBoundaryBody;
|
|
135
|
+
from_sign: string;
|
|
136
|
+
to_sign: string;
|
|
137
|
+
exact_time: string;
|
|
138
|
+
longitude: number;
|
|
139
|
+
direction: 'direct' | 'retrograde';
|
|
140
|
+
}
|
|
141
|
+
export interface SignBoundaryEventResponse {
|
|
142
|
+
date: string;
|
|
143
|
+
timezone: string;
|
|
144
|
+
calculation_timezone: string;
|
|
145
|
+
reporting_timezone: string;
|
|
146
|
+
days_ahead: number;
|
|
147
|
+
events: SignBoundaryEvent[];
|
|
148
|
+
}
|
|
127
149
|
/**
|
|
128
150
|
* Base interface for all transit data
|
|
129
151
|
*
|
|
@@ -370,6 +392,7 @@ export type PlanetId = (typeof PLANETS)[keyof typeof PLANETS];
|
|
|
370
392
|
export declare const PLANET_NAMES: {
|
|
371
393
|
[key: number]: PlanetName;
|
|
372
394
|
};
|
|
395
|
+
export declare const PLANET_IDS_BY_NAME: Record<PlanetName, PlanetId>;
|
|
373
396
|
/**
|
|
374
397
|
* Personal planets (inner planets)
|
|
375
398
|
*
|
|
@@ -400,7 +423,7 @@ export declare const OUTER_PLANETS: (8 | 6 | 5 | 7 | 9)[];
|
|
|
400
423
|
* - Juno: Partnership, commitment
|
|
401
424
|
* - Vesta: Devotion, service
|
|
402
425
|
*/
|
|
403
|
-
export declare const ASTEROIDS: (18 |
|
|
426
|
+
export declare const ASTEROIDS: (18 | 17 | 15 | 19 | 20)[];
|
|
404
427
|
/**
|
|
405
428
|
* Lunar nodes
|
|
406
429
|
*
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
export const CLASSICAL_PLANET_NAMES = [
|
|
2
|
+
'Sun',
|
|
3
|
+
'Moon',
|
|
4
|
+
'Mercury',
|
|
5
|
+
'Venus',
|
|
6
|
+
'Mars',
|
|
7
|
+
'Jupiter',
|
|
8
|
+
'Saturn',
|
|
9
|
+
];
|
|
10
|
+
export const MAJOR_PLANET_NAMES = [
|
|
11
|
+
...CLASSICAL_PLANET_NAMES,
|
|
12
|
+
'Uranus',
|
|
13
|
+
'Neptune',
|
|
14
|
+
'Pluto',
|
|
15
|
+
];
|
|
16
|
+
export const SUPPLEMENTAL_PLANET_NAMES = [
|
|
17
|
+
'Chiron',
|
|
18
|
+
'North Node (Mean)',
|
|
19
|
+
'North Node (True)',
|
|
20
|
+
'Ceres',
|
|
21
|
+
'Pallas',
|
|
22
|
+
'Juno',
|
|
23
|
+
'Vesta',
|
|
24
|
+
];
|
|
25
|
+
export const ALL_PLANET_NAMES = [...MAJOR_PLANET_NAMES, ...SUPPLEMENTAL_PLANET_NAMES];
|
|
26
|
+
export const SIGN_BOUNDARY_BODIES = MAJOR_PLANET_NAMES;
|
|
1
27
|
/**
|
|
2
28
|
* Aspect definitions with angles and default orbs
|
|
3
29
|
*
|
|
@@ -81,6 +107,25 @@ export const PLANET_NAMES = {
|
|
|
81
107
|
19: 'Juno',
|
|
82
108
|
20: 'Vesta',
|
|
83
109
|
};
|
|
110
|
+
export const PLANET_IDS_BY_NAME = {
|
|
111
|
+
Sun: PLANETS.SUN,
|
|
112
|
+
Moon: PLANETS.MOON,
|
|
113
|
+
Mercury: PLANETS.MERCURY,
|
|
114
|
+
Venus: PLANETS.VENUS,
|
|
115
|
+
Mars: PLANETS.MARS,
|
|
116
|
+
Jupiter: PLANETS.JUPITER,
|
|
117
|
+
Saturn: PLANETS.SATURN,
|
|
118
|
+
Uranus: PLANETS.URANUS,
|
|
119
|
+
Neptune: PLANETS.NEPTUNE,
|
|
120
|
+
Pluto: PLANETS.PLUTO,
|
|
121
|
+
Chiron: PLANETS.CHIRON,
|
|
122
|
+
'North Node (Mean)': PLANETS.MEAN_NODE,
|
|
123
|
+
'North Node (True)': PLANETS.TRUE_NODE,
|
|
124
|
+
Ceres: PLANETS.CERES,
|
|
125
|
+
Pallas: PLANETS.PALLAS,
|
|
126
|
+
Juno: PLANETS.JUNO,
|
|
127
|
+
Vesta: PLANETS.VESTA,
|
|
128
|
+
};
|
|
84
129
|
/**
|
|
85
130
|
* Personal planets (inner planets)
|
|
86
131
|
*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ether-to-astro v1.4.0
|
|
2
|
+
|
|
3
|
+
Suggested release type: `minor`
|
|
4
|
+
|
|
5
|
+
This release builds on `1.3.1` by adding a reusable sign-boundary event primitive for deterministic ingress and egress queries across MCP, CLI, and the shared AstroService layer. It also hardens cusp-edge behavior so exact-boundary lookups avoid false events at tangential stations and at the exclusive end of a requested local-date window.
|
|
6
|
+
|
|
7
|
+
## 1.4.0 (2026-03-29)
|
|
8
|
+
|
|
9
|
+
### New feature
|
|
10
|
+
|
|
11
|
+
- add `get_sign_boundary_events` as a stateless MCP tool for exact sign-boundary crossings across a local date window
|
|
12
|
+
- add `get-sign-boundary-events` to the CLI so sign-boundary scans are available outside MCP sessions
|
|
13
|
+
- add shared `AstroService` support and structured response types for reusable sign-boundary event lookups
|
|
14
|
+
|
|
15
|
+
### Bug fix
|
|
16
|
+
|
|
17
|
+
- skip cusp-touch roots that do not actually move the body into the adjacent sign
|
|
18
|
+
- exclude sign-boundary roots that land exactly on the exclusive end of the requested window
|
|
19
|
+
- keep error mapping and response-shape coverage aligned with the new sign-boundary surface
|
|
20
|
+
|
|
21
|
+
### Documentation Changes
|
|
22
|
+
|
|
23
|
+
- document the new sign-boundary tool in the README tool and CLI command lists
|
|
24
|
+
|
|
25
|
+
### ⚙️ Chore
|
|
26
|
+
|
|
27
|
+
- centralize supported sign-boundary bodies and planet-name constants for reuse across service logic and tests
|
|
28
|
+
- extend unit and property coverage for the new sign-boundary workflow and related shared typing
|
|
29
|
+
|
|
30
|
+
## Upgrade notes
|
|
31
|
+
|
|
32
|
+
- no breaking API changes are included in this release
|
|
33
|
+
|
|
34
|
+
## Included PRs
|
|
35
|
+
|
|
36
|
+
- #65 add sign boundary events tool
|
package/docs/releases/README.md
CHANGED
|
@@ -10,8 +10,9 @@ Conventions:
|
|
|
10
10
|
|
|
11
11
|
Current draft:
|
|
12
12
|
|
|
13
|
-
-
|
|
13
|
+
- none currently
|
|
14
14
|
|
|
15
15
|
Released notes:
|
|
16
16
|
|
|
17
|
-
- [v1.
|
|
17
|
+
- [v1.4.0 notes](./1.4.0.md)
|
|
18
|
+
- [v1.3.0 notes](./1.3.0.md)
|
package/package.json
CHANGED