@laylzj777/my-apple-apple-eventkit 1.1.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +575 -0
  2. package/LICENSE +21 -0
  3. package/README.md +304 -0
  4. package/README.zh-CN.md +270 -0
  5. package/bin/event +0 -0
  6. package/bin/event-disclaim +0 -0
  7. package/dist/bin/apple-calendar.d.ts +6 -0
  8. package/dist/bin/apple-calendar.js +8 -0
  9. package/dist/bin/apple-calendar.js.map +1 -0
  10. package/dist/bin/apple-reminders.d.ts +7 -0
  11. package/dist/bin/apple-reminders.js +9 -0
  12. package/dist/bin/apple-reminders.js.map +1 -0
  13. package/dist/bootstrap.d.ts +11 -0
  14. package/dist/bootstrap.js +44 -0
  15. package/dist/bootstrap.js.map +1 -0
  16. package/dist/index.d.ts +10 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/server/handlers.d.ts +12 -0
  20. package/dist/server/handlers.js +58 -0
  21. package/dist/server/handlers.js.map +1 -0
  22. package/dist/server/mode.d.ts +19 -0
  23. package/dist/server/mode.js +47 -0
  24. package/dist/server/mode.js.map +1 -0
  25. package/dist/server/promptAbstractions.d.ts +61 -0
  26. package/dist/server/promptAbstractions.js +140 -0
  27. package/dist/server/promptAbstractions.js.map +1 -0
  28. package/dist/server/prompts.d.ts +13 -0
  29. package/dist/server/prompts.js +461 -0
  30. package/dist/server/prompts.js.map +1 -0
  31. package/dist/server/server.d.ts +61 -0
  32. package/dist/server/server.js +112 -0
  33. package/dist/server/server.js.map +1 -0
  34. package/dist/tools/definitions.d.ts +9 -0
  35. package/dist/tools/definitions.js +272 -0
  36. package/dist/tools/definitions.js.map +1 -0
  37. package/dist/tools/handlers/calendarHandlers.d.ts +11 -0
  38. package/dist/tools/handlers/calendarHandlers.js +151 -0
  39. package/dist/tools/handlers/calendarHandlers.js.map +1 -0
  40. package/dist/tools/handlers/formatters.d.ts +24 -0
  41. package/dist/tools/handlers/formatters.js +105 -0
  42. package/dist/tools/handlers/formatters.js.map +1 -0
  43. package/dist/tools/handlers/index.d.ts +8 -0
  44. package/dist/tools/handlers/index.js +9 -0
  45. package/dist/tools/handlers/index.js.map +1 -0
  46. package/dist/tools/handlers/listHandlers.d.ts +10 -0
  47. package/dist/tools/handlers/listHandlers.js +47 -0
  48. package/dist/tools/handlers/listHandlers.js.map +1 -0
  49. package/dist/tools/handlers/reminderHandlers.d.ts +10 -0
  50. package/dist/tools/handlers/reminderHandlers.js +219 -0
  51. package/dist/tools/handlers/reminderHandlers.js.map +1 -0
  52. package/dist/tools/handlers/shared.d.ts +28 -0
  53. package/dist/tools/handlers/shared.js +57 -0
  54. package/dist/tools/handlers/shared.js.map +1 -0
  55. package/dist/tools/handlers/subtaskHandlers.d.ts +12 -0
  56. package/dist/tools/handlers/subtaskHandlers.js +113 -0
  57. package/dist/tools/handlers/subtaskHandlers.js.map +1 -0
  58. package/dist/tools/index.d.ts +10 -0
  59. package/dist/tools/index.js +74 -0
  60. package/dist/tools/index.js.map +1 -0
  61. package/dist/types/index.d.ts +263 -0
  62. package/dist/types/index.js +42 -0
  63. package/dist/types/index.js.map +1 -0
  64. package/dist/types/prompts.d.ts +88 -0
  65. package/dist/types/prompts.js +6 -0
  66. package/dist/types/prompts.js.map +1 -0
  67. package/dist/types/repository.d.ts +197 -0
  68. package/dist/types/repository.js +14 -0
  69. package/dist/types/repository.js.map +1 -0
  70. package/dist/utils/binaryValidator.d.ts +52 -0
  71. package/dist/utils/binaryValidator.js +185 -0
  72. package/dist/utils/binaryValidator.js.map +1 -0
  73. package/dist/utils/calendarRepository.d.ts +49 -0
  74. package/dist/utils/calendarRepository.js +226 -0
  75. package/dist/utils/calendarRepository.js.map +1 -0
  76. package/dist/utils/constants.d.ts +79 -0
  77. package/dist/utils/constants.js +80 -0
  78. package/dist/utils/constants.js.map +1 -0
  79. package/dist/utils/dateFiltering.d.ts +41 -0
  80. package/dist/utils/dateFiltering.js +129 -0
  81. package/dist/utils/dateFiltering.js.map +1 -0
  82. package/dist/utils/dateUtils.d.ts +27 -0
  83. package/dist/utils/dateUtils.js +71 -0
  84. package/dist/utils/dateUtils.js.map +1 -0
  85. package/dist/utils/errorHandling.d.ts +27 -0
  86. package/dist/utils/errorHandling.js +88 -0
  87. package/dist/utils/errorHandling.js.map +1 -0
  88. package/dist/utils/eventCli.d.ts +40 -0
  89. package/dist/utils/eventCli.js +294 -0
  90. package/dist/utils/eventCli.js.map +1 -0
  91. package/dist/utils/helpers.d.ts +35 -0
  92. package/dist/utils/helpers.js +61 -0
  93. package/dist/utils/helpers.js.map +1 -0
  94. package/dist/utils/looseDateParser.d.ts +19 -0
  95. package/dist/utils/looseDateParser.js +149 -0
  96. package/dist/utils/looseDateParser.js.map +1 -0
  97. package/dist/utils/projectUtils.d.ts +13 -0
  98. package/dist/utils/projectUtils.js +92 -0
  99. package/dist/utils/projectUtils.js.map +1 -0
  100. package/dist/utils/reminderDateParser.d.ts +8 -0
  101. package/dist/utils/reminderDateParser.js +89 -0
  102. package/dist/utils/reminderDateParser.js.map +1 -0
  103. package/dist/utils/reminderRepository.d.ts +33 -0
  104. package/dist/utils/reminderRepository.js +262 -0
  105. package/dist/utils/reminderRepository.js.map +1 -0
  106. package/dist/utils/subtaskUtils.d.ts +103 -0
  107. package/dist/utils/subtaskUtils.js +253 -0
  108. package/dist/utils/subtaskUtils.js.map +1 -0
  109. package/dist/utils/tagUtils.d.ts +58 -0
  110. package/dist/utils/tagUtils.js +177 -0
  111. package/dist/utils/tagUtils.js.map +1 -0
  112. package/dist/utils/timeHelpers.d.ts +40 -0
  113. package/dist/utils/timeHelpers.js +136 -0
  114. package/dist/utils/timeHelpers.js.map +1 -0
  115. package/dist/validation/schemas.d.ts +385 -0
  116. package/dist/validation/schemas.js +579 -0
  117. package/dist/validation/schemas.js.map +1 -0
  118. package/package.json +96 -0
  119. package/scripts/build-event.mjs +513 -0
  120. package/scripts/disclaim.c +69 -0
  121. package/scripts/event-Info.plist +29 -0
  122. package/scripts/event.entitlements +10 -0
  123. package/scripts/postinstall.mjs +91 -0
@@ -0,0 +1,294 @@
1
+ /**
2
+ * @fileoverview `event` CLI execution wrapper
3
+ * @module utils/eventCli
4
+ * @description Spawns the vendored `event` Swift binary (FradSer/event) and
5
+ * translates its stdout / stderr / exit code into JSON results, plain-text
6
+ * results, or domain-specific errors. `event` outputs raw JSON to stdout and
7
+ * writes `Error: <message>` to stderr with a non-zero exit code on failure.
8
+ */
9
+ import { execFile } from 'node:child_process';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { findSecureBinaryPath, getEnvironmentBinaryConfig, } from './binaryValidator.js';
13
+ import { FILE_SYSTEM } from './constants.js';
14
+ import { CliUserError } from './errorHandling.js';
15
+ import { bufferToString } from './helpers.js';
16
+ import { findProjectRoot } from './projectUtils.js';
17
+ // A no-shim launch is cached too (`disclaimFingerprint: null`) and stays
18
+ // valid only while the shim's canonical path remains absent — so a shim that
19
+ // appears after a rebuild is picked up on the next call, and the no-shim
20
+ // path doesn't re-run full validation (which hashes the ~50 MB binary when
21
+ // SWIFT_BINARY_HASH is pinned) on every tool call.
22
+ let cachedLaunch = null;
23
+ // Emitted once per process so a missing/invalid shim (which silently reverts
24
+ // EventKit prompts to host-app attribution — the issue #93 failure mode) is
25
+ // diagnosable from the host's MCP server logs.
26
+ let warnedShimUnavailable = false;
27
+ /**
28
+ * Selects how EventKit permission prompts are attributed, via the
29
+ * `EVENT_TCC_ATTRIBUTION` env var (case-insensitive):
30
+ *
31
+ * - `self` (default): spawn `event` through the `event-disclaim` shim so it
32
+ * becomes its own TCC-responsible process. The macOS prompt then shows the
33
+ * name embedded in `event`'s signed Info.plist (`CFBundleName`), independent
34
+ * of which app launched the MCP server, and one grant covers every host.
35
+ * - `host`: bypass the shim and spawn `event` directly. macOS attributes the
36
+ * prompt to the *host* application that launched the server (e.g. the MCP
37
+ * client), so the dialog dynamically shows the current host app's name.
38
+ * Requires the host app to declare the relevant EventKit usage strings.
39
+ *
40
+ * Any unrecognized value falls back to `self`.
41
+ */
42
+ function useHostAttribution() {
43
+ return ((process.env.EVENT_TCC_ATTRIBUTION ?? '').trim().toLowerCase() === 'host');
44
+ }
45
+ /** Clears the cached binary path (for testing). */
46
+ export function clearEventBinaryPathCache() {
47
+ cachedLaunch = null;
48
+ warnedShimUnavailable = false;
49
+ }
50
+ const fingerprintFor = (filePath) => {
51
+ try {
52
+ const stat = fs.statSync(filePath);
53
+ return { ino: stat.ino, mtimeMs: stat.mtimeMs, size: stat.size };
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ };
59
+ const fingerprintMatches = (a, b) => a !== null &&
60
+ b !== null &&
61
+ a.ino === b.ino &&
62
+ a.mtimeMs === b.mtimeMs &&
63
+ a.size === b.size;
64
+ const execFilePromise = (cliPath, args) => new Promise((resolve) => {
65
+ execFile(cliPath, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
66
+ // Hand both branches to the caller so it can decide based on stderr
67
+ // content rather than the exit code alone — `event` emits structured
68
+ // EventCLIError messages on stderr regardless of which exit code path
69
+ // ArgumentParser chose (1 for app errors, 64 for usage errors).
70
+ resolve({ result: { stdout, stderr }, error: error ?? null });
71
+ });
72
+ });
73
+ /**
74
+ * Detects an `event` permission error and which EventKit domain it belongs to.
75
+ *
76
+ * `event`'s error format is `Error: Permission denied: <domain-specific text>`
77
+ * where the domain word ("Reminders" / "Reminder" or "Calendar"/"Calendars")
78
+ * always appears in the second half of the message. We match anchored on the
79
+ * "Permission denied" prefix so we never misclassify the unrelated phrase
80
+ * "permission" used in normal output.
81
+ */
82
+ // An ordered array (not a `Record`) because iteration order matters: a
83
+ // defensive message that names both domains is attributed to whichever
84
+ // pattern appears first here, and `Record`/`Object.entries` iteration order
85
+ // isn't guaranteed by the type system even though it happens to match
86
+ // insertion order at runtime.
87
+ const PERMISSION_DOMAIN_PATTERNS = [
88
+ ['reminders', /Permission denied:[\s\S]*?reminders?/i],
89
+ ['calendars', /Permission denied:[\s\S]*?calendars?/i],
90
+ ];
91
+ function detectPermissionDomain(message) {
92
+ for (const [domain, pattern] of PERMISSION_DOMAIN_PATTERNS) {
93
+ if (pattern.test(message)) {
94
+ return domain;
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+ /** Custom error class for permission-related failures from the `event` CLI. */
100
+ export class CliPermissionError extends Error {
101
+ constructor(message, domain) {
102
+ super(message);
103
+ this.domain = domain;
104
+ this.name = 'CliPermissionError';
105
+ }
106
+ }
107
+ /**
108
+ * Pulls the meaningful message out of `event`'s stderr. ArgumentParser prepends
109
+ * `Error: ` to every EventCLIError and to its own usage errors, so we strip
110
+ * that and trim trailing whitespace. The boolean signals whether the stderr
111
+ * looked like a structured event-emitted error or some other failure mode.
112
+ */
113
+ function extractStderrMessage(stderr) {
114
+ const trimmed = stderr.replace(/\r?\n/g, '\n').trim();
115
+ if (!trimmed)
116
+ return { message: '', hadErrorPrefix: false };
117
+ if (trimmed.startsWith('Error: ')) {
118
+ return {
119
+ message: trimmed.slice('Error: '.length),
120
+ hadErrorPrefix: true,
121
+ };
122
+ }
123
+ return { message: trimmed, hadErrorPrefix: false };
124
+ }
125
+ function throwForStderr(stderr) {
126
+ const { message, hadErrorPrefix } = extractStderrMessage(stderr);
127
+ if (!message) {
128
+ throw new Error('event execution failed: unknown error');
129
+ }
130
+ // The disclaim shim reports its own spawn failures as
131
+ // `event-disclaim: <detail>` (no "Error: " prefix). Surface them verbatim
132
+ // as user-actionable errors — otherwise production error formatting
133
+ // collapses them into a generic "System error occurred".
134
+ if (message.startsWith('event-disclaim:')) {
135
+ throw new CliUserError(message);
136
+ }
137
+ // Only structured "Error: ..." stderr is treated as a user-actionable
138
+ // CliUserError or permission error. Anything else (panics, OS-level
139
+ // failures, etc.) is wrapped so the host surface mentions the `event`
140
+ // binary instead of attributing the message to our own code.
141
+ if (!hadErrorPrefix) {
142
+ throw new Error(`event execution failed: ${message}`);
143
+ }
144
+ const domain = detectPermissionDomain(message);
145
+ if (domain) {
146
+ throw new CliPermissionError(message, domain);
147
+ }
148
+ throw new CliUserError(message);
149
+ }
150
+ async function runEventCli(launch, args) {
151
+ // Route through the disclaim shim when it exists: `event-disclaim <event>
152
+ // <args…>` re-execs `event` with TCC responsibility disclaimed.
153
+ const file = launch.disclaimPath ?? launch.cliPath;
154
+ const argv = launch.disclaimPath ? [launch.cliPath, ...args] : args;
155
+ const { result, error } = await execFilePromise(file, argv);
156
+ const stderr = bufferToString(result.stderr) ?? '';
157
+ if (error) {
158
+ if (stderr) {
159
+ throwForStderr(stderr);
160
+ }
161
+ const msg = error.message || String(error);
162
+ throw new Error(`event execution failed: ${msg}`);
163
+ }
164
+ return result;
165
+ }
166
+ function resolveLaunchOrThrow() {
167
+ const hostAttribution = useHostAttribution();
168
+ if (cachedLaunch && cachedLaunch.hostAttribution === hostAttribution) {
169
+ const { launch } = cachedLaunch;
170
+ const cliOk = fingerprintMatches(cachedLaunch.cliFingerprint, fingerprintFor(launch.cliPath));
171
+ const disclaimOk = launch.disclaimPath
172
+ ? fingerprintMatches(cachedLaunch.disclaimFingerprint, fingerprintFor(launch.disclaimPath))
173
+ : fingerprintFor(cachedLaunch.disclaimCanonicalPath) === null;
174
+ if (cliOk && disclaimOk) {
175
+ return launch;
176
+ }
177
+ }
178
+ cachedLaunch = null;
179
+ const projectRoot = findProjectRoot();
180
+ const binaryName = FILE_SYSTEM.SWIFT_BINARY_NAME;
181
+ const canonicalPath = path.join(projectRoot, 'bin', binaryName);
182
+ // Restrict the validator's suffix matcher to this one absolute path so a
183
+ // misconfigured allowlist can't accept `/usr/local/bin/event` or any other
184
+ // `bin/event` on disk by accident.
185
+ const config = {
186
+ ...getEnvironmentBinaryConfig(),
187
+ allowedPaths: [canonicalPath],
188
+ };
189
+ const { path: cliPath } = findSecureBinaryPath([canonicalPath], config);
190
+ if (!cliPath) {
191
+ throw new CliUserError(`event CLI binary not found at ${canonicalPath}.
192
+
193
+ The vendored \`event\` Swift binary is normally built automatically by the
194
+ postinstall script, but that step may have been skipped or failed (for example
195
+ when the package was installed without devDependencies, on a non-macOS host,
196
+ or before Xcode Command Line Tools were available).
197
+
198
+ To build it manually, clone the repository and run a local build:
199
+ git clone --recurse-submodules https://github.com/fradser/mcp-server-apple-events.git
200
+ cd mcp-server-apple-events
201
+ pnpm install
202
+ pnpm build
203
+
204
+ Then use the local path in your Claude Desktop config:
205
+ "command": "node",
206
+ "args": ["/absolute/path/to/mcp-server-apple-events/bin/run.cjs"]`);
207
+ }
208
+ const disclaimCanonicalPath = path.join(projectRoot, 'bin', FILE_SYSTEM.DISCLAIM_BINARY_NAME);
209
+ // Host-attribution mode intentionally bypasses the disclaim shim: spawning
210
+ // `event` directly lets macOS attribute the EventKit prompt to the host app
211
+ // that launched the MCP server, so the dialog shows that app's name.
212
+ let disclaimPath = null;
213
+ if (!hostAttribution) {
214
+ ({ path: disclaimPath } = findSecureBinaryPath([disclaimCanonicalPath], {
215
+ ...getEnvironmentBinaryConfig(),
216
+ // SWIFT_BINARY_HASH pins bin/event, not the shim — carrying it over here
217
+ // would reject the shim on every strict-mode install and silently revert
218
+ // to host-attributed prompts. The shim gets its own optional pin.
219
+ expectedHash: process.env.SWIFT_DISCLAIM_BINARY_HASH,
220
+ allowedPaths: [disclaimCanonicalPath],
221
+ }));
222
+ if (!disclaimPath &&
223
+ !warnedShimUnavailable &&
224
+ process.env.NODE_ENV !== 'test') {
225
+ warnedShimUnavailable = true;
226
+ console.error(`event-disclaim shim not found or failed validation at ${disclaimCanonicalPath}; ` +
227
+ 'spawning event directly. EventKit permission prompts will be attributed ' +
228
+ 'to the host app instead of event (issue #93). Rebuild with `pnpm build` ' +
229
+ 'to restore the shim.');
230
+ }
231
+ }
232
+ const launch = { cliPath, disclaimPath };
233
+ const cliFingerprint = fingerprintFor(cliPath);
234
+ const disclaimFingerprint = disclaimPath
235
+ ? fingerprintFor(disclaimPath)
236
+ : null;
237
+ // A shim that resolved but vanished before fingerprinting (race) leaves
238
+ // disclaimFingerprint null with disclaimPath set — don't cache that; the
239
+ // next call re-resolves.
240
+ if (cliFingerprint && (disclaimPath === null || disclaimFingerprint)) {
241
+ cachedLaunch = {
242
+ launch,
243
+ disclaimCanonicalPath,
244
+ cliFingerprint,
245
+ disclaimFingerprint,
246
+ hostAttribution,
247
+ };
248
+ }
249
+ return launch;
250
+ }
251
+ /**
252
+ * Executes the `event` binary and parses its stdout as raw JSON.
253
+ *
254
+ * @template T - Expected JSON type emitted by `event`
255
+ * @param args - Full argv (including subcommand and `--json` where supported)
256
+ * @returns Parsed JSON value
257
+ * @throws {CliPermissionError} on EventKit permission failure (domain-typed)
258
+ * @throws {CliUserError} on application errors surfaced by `event` (Not found,
259
+ * Invalid input, ArgumentParser usage errors)
260
+ * @throws {Error} when stdout is empty or unparseable
261
+ *
262
+ * @security
263
+ * - Uses `execFile` (not `exec`) so shell metacharacters in argv are inert.
264
+ * - Argv is passed as an array; each token is delivered to the binary verbatim
265
+ * via `execve()`, preventing argument-boundary injection.
266
+ * - Binary path is validated against an allowlist tied to the project root.
267
+ */
268
+ export async function executeEventCliJson(args) {
269
+ const launch = resolveLaunchOrThrow();
270
+ const { stdout } = await runEventCli(launch, args);
271
+ const normalized = bufferToString(stdout);
272
+ if (!normalized) {
273
+ throw new Error('event execution failed: Empty CLI output');
274
+ }
275
+ try {
276
+ return JSON.parse(normalized);
277
+ }
278
+ catch (error) {
279
+ const detail = error instanceof Error ? error.message : String(error);
280
+ throw new Error(`event execution failed: Invalid CLI output - ${detail}`);
281
+ }
282
+ }
283
+ /**
284
+ * Executes the `event` binary and returns its trimmed stdout as plain text.
285
+ * Used for commands that emit a success message instead of JSON (e.g.
286
+ * `event reminders delete` returns "Reminder deleted successfully").
287
+ */
288
+ export async function executeEventCliPlain(args) {
289
+ const launch = resolveLaunchOrThrow();
290
+ const { stdout } = await runEventCli(launch, args);
291
+ const normalized = bufferToString(stdout) ?? '';
292
+ return normalized.trim();
293
+ }
294
+ //# sourceMappingURL=eventCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventCli.js","sourceRoot":"","sources":["../../src/utils/eventCli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * helpers.ts
3
+ * General utility functions for common operations
4
+ */
5
+ /**
6
+ * CLI argument building utilities
7
+ */
8
+ /**
9
+ * Adds an optional string argument to the args array if the value is defined
10
+ */
11
+ export declare function addOptionalArg(args: string[], flag: string, value: string | undefined): void;
12
+ /**
13
+ * Adds an optional number argument to the args array if the value is defined
14
+ */
15
+ export declare function addOptionalNumberArg(args: string[], flag: string, value: number | undefined): void;
16
+ /**
17
+ * Type conversion utilities
18
+ */
19
+ /**
20
+ * Converts null values to undefined for optional fields
21
+ * This is useful when converting from JSON (which uses null) to TypeScript types (which use undefined)
22
+ */
23
+ export declare function nullToUndefined<T>(obj: T, fields: (keyof T)[]): T;
24
+ /**
25
+ * String manipulation utilities
26
+ */
27
+ /**
28
+ * Converts Buffer or string data to string, handling null/undefined values
29
+ */
30
+ export declare function bufferToString(data?: string | Buffer | null): string | null;
31
+ /**
32
+ * Formats multiline notes for markdown display by indenting continuation lines
33
+ * Replaces newlines with newline + indentation to maintain proper formatting
34
+ */
35
+ export declare function formatMultilineNotes(notes: string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * helpers.ts
3
+ * General utility functions for common operations
4
+ */
5
+ /**
6
+ * CLI argument building utilities
7
+ */
8
+ /**
9
+ * Adds an optional string argument to the args array if the value is defined
10
+ */
11
+ export function addOptionalArg(args, flag, value) {
12
+ if (value !== undefined) {
13
+ args.push(flag, value);
14
+ }
15
+ }
16
+ /**
17
+ * Adds an optional number argument to the args array if the value is defined
18
+ */
19
+ export function addOptionalNumberArg(args, flag, value) {
20
+ if (value !== undefined) {
21
+ args.push(flag, String(value));
22
+ }
23
+ }
24
+ /**
25
+ * Type conversion utilities
26
+ */
27
+ /**
28
+ * Converts null values to undefined for optional fields
29
+ * This is useful when converting from JSON (which uses null) to TypeScript types (which use undefined)
30
+ */
31
+ export function nullToUndefined(obj, fields) {
32
+ const result = { ...obj };
33
+ for (const field of fields) {
34
+ const fieldKey = String(field);
35
+ if (result[fieldKey] === null) {
36
+ result[fieldKey] = undefined;
37
+ }
38
+ }
39
+ return result;
40
+ }
41
+ /**
42
+ * String manipulation utilities
43
+ */
44
+ /**
45
+ * Converts Buffer or string data to string, handling null/undefined values
46
+ */
47
+ export function bufferToString(data) {
48
+ if (typeof data === 'string')
49
+ return data;
50
+ if (Buffer.isBuffer(data))
51
+ return data.toString('utf8');
52
+ return data ?? null;
53
+ }
54
+ /**
55
+ * Formats multiline notes for markdown display by indenting continuation lines
56
+ * Replaces newlines with newline + indentation to maintain proper formatting
57
+ */
58
+ export function formatMultilineNotes(notes) {
59
+ return notes.replace(/\n/g, '\n ');
60
+ }
61
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAc,EACd,IAAY,EACZ,KAAyB;IAEzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAc,EACd,IAAY,EACZ,KAAyB;IAEzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAI,GAAM,EAAE,MAAmB;IAC5D,MAAM,MAAM,GAAG,EAAE,GAAG,GAAG,EAA6B,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9B,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,MAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAA6B;IAC1D,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * looseDateParser.ts
3
+ * 宽容日期输入归一化(fork 增量)。
4
+ *
5
+ * 上游 schema 只接受 'YYYY-MM-DD'、'YYYY-MM-DD HH:mm:ss'、ISO 8601。实际会话里模型
6
+ * (尤其从旧日历 MCP 的工具习惯迁移过来时)还会给出英文自然语言("15 March 2025 at
7
+ * 2:00 PM")或中文日期("2026年7月11日 下午2点")。旧包对这类输入把时刻静默归零,
8
+ * 事件落在 00:00——本模块在 zod 校验前把这些写法归一成严格格式,归一不了的原样
9
+ * 返回,让 zod 用统一的格式报错引导模型改写。
10
+ *
11
+ * 归一始终产出**本地时间**('YYYY-MM-DD[ HH:mm:ss]');已符合严格格式的输入
12
+ * (含带时区的 ISO)原样透传,不做二次加工。
13
+ */
14
+ export declare const STRICT_DATE_PATTERN: RegExp;
15
+ /**
16
+ * 归一化宽松日期输入。返回严格格式字符串;无法归一时返回 trim 后的原输入
17
+ * (由下游 zod 统一报格式错误)。
18
+ */
19
+ export declare function normalizeLooseDateInput(raw: string): string;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * looseDateParser.ts
3
+ * 宽容日期输入归一化(fork 增量)。
4
+ *
5
+ * 上游 schema 只接受 'YYYY-MM-DD'、'YYYY-MM-DD HH:mm:ss'、ISO 8601。实际会话里模型
6
+ * (尤其从旧日历 MCP 的工具习惯迁移过来时)还会给出英文自然语言("15 March 2025 at
7
+ * 2:00 PM")或中文日期("2026年7月11日 下午2点")。旧包对这类输入把时刻静默归零,
8
+ * 事件落在 00:00——本模块在 zod 校验前把这些写法归一成严格格式,归一不了的原样
9
+ * 返回,让 zod 用统一的格式报错引导模型改写。
10
+ *
11
+ * 归一始终产出**本地时间**('YYYY-MM-DD[ HH:mm:ss]');已符合严格格式的输入
12
+ * (含带时区的 ISO)原样透传,不做二次加工。
13
+ */
14
+ // 与 validation/schemas.ts 的 DATE_PATTERN 保持一致(schemas.ts 从这里导入,单一真源)。
15
+ export const STRICT_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?(?:\.\d{1,9})?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
16
+ const ENGLISH_WEEKDAY_PREFIX = /^(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday),?\s+/i;
17
+ const pad = (n) => String(n).padStart(2, '0');
18
+ /** 中文时段词 → 是否按 PM 语义换算(凌晨/早上/上午按 AM)。 */
19
+ const CHINESE_PM_WORDS = new Set(['下午', '晚上', '傍晚', '中午']);
20
+ function applyMeridiem(hour, isPm) {
21
+ if (isPm && hour < 12)
22
+ return hour + 12;
23
+ if (!isPm && hour === 12)
24
+ return 0;
25
+ return hour;
26
+ }
27
+ /**
28
+ * 从字符串里抽取时刻。按可信度顺序尝试:
29
+ * 1. 英文 "at 2:00 PM" / "at 14:00" / "at 2 PM"
30
+ * 2. 中文 "下午2点" / "下午2:30" / "14点30分" / "14点"
31
+ * 3. 裸 "HH:mm(:ss)"(AM/PM 可选)
32
+ */
33
+ function extractTime(input) {
34
+ // 1. "at 2:00 PM" / "at 14:00"
35
+ let m = input.match(/\bat\s+(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(am|pm)?/i);
36
+ if (m?.[1] !== undefined && m[2] !== undefined) {
37
+ const meridiem = m[4]?.toLowerCase();
38
+ return {
39
+ hour: meridiem === undefined
40
+ ? Number(m[1])
41
+ : applyMeridiem(Number(m[1]), meridiem === 'pm'),
42
+ minute: Number(m[2]),
43
+ second: m[3] === undefined ? 0 : Number(m[3]),
44
+ matched: m[0],
45
+ };
46
+ }
47
+ // 1b. "at 2 PM"(无分钟,必须带 AM/PM 才可信)
48
+ m = input.match(/\bat\s+(\d{1,2})\s*(am|pm)\b/i);
49
+ if (m?.[1] !== undefined && m[2] !== undefined) {
50
+ return {
51
+ hour: applyMeridiem(Number(m[1]), m[2].toLowerCase() === 'pm'),
52
+ minute: 0,
53
+ second: 0,
54
+ matched: m[0],
55
+ };
56
+ }
57
+ // 2. 中文时段 + 时刻:"下午2点" / "下午2:30" / "晚上8点15分"
58
+ // 分钟数字在分隔符(点/时/:)之后可缺省——"下午2点"须整体命中并消费掉"点"。
59
+ m = input.match(/(凌晨|早上|上午|中午|下午|傍晚|晚上)\s*(\d{1,2})(?:[点时::](\d{1,2})?)?分?/);
60
+ if (m?.[1] !== undefined && m[2] !== undefined) {
61
+ return {
62
+ hour: applyMeridiem(Number(m[2]), CHINESE_PM_WORDS.has(m[1])),
63
+ minute: m[3] === undefined ? 0 : Number(m[3]),
64
+ second: 0,
65
+ matched: m[0],
66
+ };
67
+ }
68
+ // 2b. 24 小时制中文:"14点30分" / "14点"
69
+ m = input.match(/(\d{1,2})[点时](?:(\d{1,2})分?)?/);
70
+ if (m?.[1] !== undefined) {
71
+ return {
72
+ hour: Number(m[1]),
73
+ minute: m[2] === undefined ? 0 : Number(m[2]),
74
+ second: 0,
75
+ matched: m[0],
76
+ };
77
+ }
78
+ // 3. 裸 "HH:mm(:ss)",AM/PM 可选("11 July 2026 14:30" / "2:00 PM")
79
+ m = input.match(/(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(am|pm)?\b/i);
80
+ if (m?.[1] !== undefined && m[2] !== undefined) {
81
+ const meridiem = m[4]?.toLowerCase();
82
+ return {
83
+ hour: meridiem === undefined
84
+ ? Number(m[1])
85
+ : applyMeridiem(Number(m[1]), meridiem === 'pm'),
86
+ minute: Number(m[2]),
87
+ second: m[3] === undefined ? 0 : Number(m[3]),
88
+ matched: m[0],
89
+ };
90
+ }
91
+ return undefined;
92
+ }
93
+ /** 解析纯日期部分 → [year, month, day]。不做溢出自纠(2月30日返回 undefined)。 */
94
+ function parseDateOnly(input) {
95
+ const s = input.trim().replace(/[,,]\s*$/, '');
96
+ if (!s)
97
+ return undefined;
98
+ // 中文:"2026年7月11日" / "2026年7月11号"
99
+ let m = s.match(/^(\d{4})年(\d{1,2})月(\d{1,2})[日号]?$/);
100
+ // 数字分隔:"2026-7-11" / "2026/7/11" / "2026.7.11"
101
+ m = m ?? s.match(/^(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})$/);
102
+ if (m?.[1] !== undefined && m[2] !== undefined && m[3] !== undefined) {
103
+ return validateYmd(Number(m[1]), Number(m[2]), Number(m[3]));
104
+ }
105
+ // 英文自然语言:"15 March 2025" / "March 15, 2025"——交给 Date 解析,
106
+ // 但要求字符串里含英文月份词,避免 Date 对任意数字串的宽松误解析。
107
+ if (/\b(january|february|march|april|may|june|july|august|september|october|november|december)\b/i.test(s)) {
108
+ const parsed = new Date(s);
109
+ if (!Number.isNaN(parsed.getTime())) {
110
+ return [parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate()];
111
+ }
112
+ }
113
+ return undefined;
114
+ }
115
+ /** 用本地 Date 构造校验年月日真实存在(拒绝 JS 自动进位,如 2 月 30 日)。 */
116
+ function validateYmd(year, month, day) {
117
+ const d = new Date(year, month - 1, day);
118
+ if (d.getFullYear() !== year ||
119
+ d.getMonth() !== month - 1 ||
120
+ d.getDate() !== day) {
121
+ return undefined;
122
+ }
123
+ return [year, month, day];
124
+ }
125
+ /**
126
+ * 归一化宽松日期输入。返回严格格式字符串;无法归一时返回 trim 后的原输入
127
+ * (由下游 zod 统一报格式错误)。
128
+ */
129
+ export function normalizeLooseDateInput(raw) {
130
+ const trimmed = raw.trim();
131
+ if (STRICT_DATE_PATTERN.test(trimmed))
132
+ return trimmed;
133
+ let rest = trimmed.replace(ENGLISH_WEEKDAY_PREFIX, '');
134
+ const time = extractTime(rest);
135
+ if (time) {
136
+ if (time.hour > 23 || time.minute > 59 || time.second > 59)
137
+ return trimmed;
138
+ rest = rest.replace(time.matched, ' ');
139
+ }
140
+ const ymd = parseDateOnly(rest.replace(/\s+/g, ' ').trim());
141
+ if (!ymd)
142
+ return trimmed;
143
+ const [year, month, day] = ymd;
144
+ const datePart = `${year}-${pad(month)}-${pad(day)}`;
145
+ if (!time)
146
+ return datePart;
147
+ return `${datePart} ${pad(time.hour)}:${pad(time.minute)}:${pad(time.second)}`;
148
+ }
149
+ //# sourceMappingURL=looseDateParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"looseDateParser.js","sourceRoot":"","sources":["../../src/utils/looseDateParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * projectUtils.ts
3
+ * Shared utilities for project-related operations
4
+ */
5
+ /**
6
+ * Finds the project root directory by looking for package.json
7
+ * @param maxDepth - Maximum directory levels to traverse upward
8
+ * @returns Project root directory path
9
+ * @throws Error if project root cannot be found
10
+ */
11
+ export declare function findProjectRoot(maxDepth?: 10): string;
12
+ /** Test helper — drops the cached project root so a fresh resolve happens. */
13
+ export declare function __clearProjectRootCache(): void;