@laitszkin/apollo-toolkit 3.13.2 → 3.14.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 (154) hide show
  1. package/AGENTS.md +7 -7
  2. package/CHANGELOG.md +27 -0
  3. package/CLAUDE.md +8 -8
  4. package/analyse-app-logs/SKILL.md +3 -3
  5. package/bin/apollo-toolkit.ts +7 -0
  6. package/codex/codex-memory-manager/SKILL.md +2 -2
  7. package/codex/learn-skill-from-conversations/SKILL.md +3 -3
  8. package/dist/bin/apollo-toolkit.d.ts +2 -0
  9. package/dist/bin/apollo-toolkit.js +7 -0
  10. package/dist/lib/cli.d.ts +41 -0
  11. package/dist/lib/cli.js +655 -0
  12. package/dist/lib/installer.d.ts +59 -0
  13. package/dist/lib/installer.js +404 -0
  14. package/dist/lib/tool-runner.d.ts +19 -0
  15. package/dist/lib/tool-runner.js +536 -0
  16. package/dist/lib/tools/architecture.d.ts +2 -0
  17. package/dist/lib/tools/architecture.js +34 -0
  18. package/dist/lib/tools/create-specs.d.ts +2 -0
  19. package/dist/lib/tools/create-specs.js +175 -0
  20. package/dist/lib/tools/docs-to-voice.d.ts +2 -0
  21. package/dist/lib/tools/docs-to-voice.js +705 -0
  22. package/dist/lib/tools/enforce-video-aspect-ratio.d.ts +2 -0
  23. package/dist/lib/tools/enforce-video-aspect-ratio.js +312 -0
  24. package/dist/lib/tools/extract-conversations.d.ts +2 -0
  25. package/dist/lib/tools/extract-conversations.js +105 -0
  26. package/dist/lib/tools/extract-pdf-text.d.ts +2 -0
  27. package/dist/lib/tools/extract-pdf-text.js +92 -0
  28. package/dist/lib/tools/filter-logs.d.ts +2 -0
  29. package/dist/lib/tools/filter-logs.js +94 -0
  30. package/dist/lib/tools/find-github-issues.d.ts +2 -0
  31. package/dist/lib/tools/find-github-issues.js +176 -0
  32. package/dist/lib/tools/generate-storyboard-images.d.ts +2 -0
  33. package/dist/lib/tools/generate-storyboard-images.js +419 -0
  34. package/dist/lib/tools/log-cli-utils.d.ts +35 -0
  35. package/dist/lib/tools/log-cli-utils.js +233 -0
  36. package/dist/lib/tools/open-github-issue.d.ts +2 -0
  37. package/dist/lib/tools/open-github-issue.js +750 -0
  38. package/dist/lib/tools/read-github-issue.d.ts +2 -0
  39. package/dist/lib/tools/read-github-issue.js +134 -0
  40. package/dist/lib/tools/render-error-book.d.ts +2 -0
  41. package/dist/lib/tools/render-error-book.js +265 -0
  42. package/dist/lib/tools/render-katex.d.ts +2 -0
  43. package/dist/lib/tools/render-katex.js +294 -0
  44. package/dist/lib/tools/review-threads.d.ts +2 -0
  45. package/dist/lib/tools/review-threads.js +491 -0
  46. package/dist/lib/tools/search-logs.d.ts +2 -0
  47. package/dist/lib/tools/search-logs.js +164 -0
  48. package/dist/lib/tools/sync-memory-index.d.ts +2 -0
  49. package/dist/lib/tools/sync-memory-index.js +113 -0
  50. package/dist/lib/tools/validate-openai-agent-config.d.ts +2 -0
  51. package/dist/lib/tools/validate-openai-agent-config.js +184 -0
  52. package/dist/lib/tools/validate-skill-frontmatter.d.ts +2 -0
  53. package/dist/lib/tools/validate-skill-frontmatter.js +118 -0
  54. package/dist/lib/types.d.ts +82 -0
  55. package/dist/lib/types.js +2 -0
  56. package/dist/lib/updater.d.ts +34 -0
  57. package/dist/lib/updater.js +112 -0
  58. package/dist/lib/utils/format.d.ts +2 -0
  59. package/dist/lib/utils/format.js +6 -0
  60. package/dist/lib/utils/terminal.d.ts +12 -0
  61. package/dist/lib/utils/terminal.js +26 -0
  62. package/docs-to-voice/SKILL.md +0 -1
  63. package/generate-spec/SKILL.md +1 -1
  64. package/katex/SKILL.md +1 -2
  65. package/lib/cli.ts +780 -0
  66. package/lib/installer.ts +466 -0
  67. package/lib/tool-runner.ts +561 -0
  68. package/lib/tools/architecture.ts +34 -0
  69. package/lib/tools/create-specs.ts +204 -0
  70. package/lib/tools/docs-to-voice.ts +799 -0
  71. package/lib/tools/enforce-video-aspect-ratio.ts +368 -0
  72. package/lib/tools/extract-conversations.ts +114 -0
  73. package/lib/tools/extract-pdf-text.ts +99 -0
  74. package/lib/tools/filter-logs.ts +118 -0
  75. package/lib/tools/find-github-issues.ts +211 -0
  76. package/lib/tools/generate-storyboard-images.ts +455 -0
  77. package/lib/tools/log-cli-utils.ts +262 -0
  78. package/lib/tools/open-github-issue.ts +930 -0
  79. package/lib/tools/read-github-issue.ts +179 -0
  80. package/lib/tools/render-error-book.ts +300 -0
  81. package/lib/tools/render-katex.ts +325 -0
  82. package/lib/tools/review-threads.ts +590 -0
  83. package/lib/tools/search-logs.ts +200 -0
  84. package/lib/tools/sync-memory-index.ts +114 -0
  85. package/lib/tools/validate-openai-agent-config.ts +209 -0
  86. package/lib/tools/validate-skill-frontmatter.ts +124 -0
  87. package/lib/types.ts +90 -0
  88. package/lib/updater.ts +165 -0
  89. package/lib/utils/format.ts +7 -0
  90. package/lib/utils/terminal.ts +22 -0
  91. package/open-github-issue/SKILL.md +2 -2
  92. package/optimise-skill/SKILL.md +1 -1
  93. package/package.json +13 -4
  94. package/resources/project-architecture/assets/architecture.css +764 -0
  95. package/resources/project-architecture/assets/viewer.client.js +144 -0
  96. package/resources/project-architecture/index.html +42 -0
  97. package/review-spec-related-changes/SKILL.md +1 -1
  98. package/solve-issues-found-during-review/SKILL.md +2 -1
  99. package/tsconfig.json +28 -0
  100. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  101. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  102. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  103. package/analyse-app-logs/scripts/filter_logs_by_time.py +0 -64
  104. package/analyse-app-logs/scripts/log_cli_utils.py +0 -112
  105. package/analyse-app-logs/scripts/search_logs.py +0 -137
  106. package/analyse-app-logs/tests/test_filter_logs_by_time.py +0 -95
  107. package/analyse-app-logs/tests/test_search_logs.py +0 -100
  108. package/codex/codex-memory-manager/scripts/extract_recent_conversations.py +0 -369
  109. package/codex/codex-memory-manager/scripts/sync_memory_index.py +0 -130
  110. package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +0 -177
  111. package/codex/codex-memory-manager/tests/test_memory_template.py +0 -37
  112. package/codex/codex-memory-manager/tests/test_sync_memory_index.py +0 -84
  113. package/codex/learn-skill-from-conversations/scripts/extract_recent_conversations.py +0 -369
  114. package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +0 -177
  115. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  116. package/docs-to-voice/scripts/docs_to_voice.py +0 -1385
  117. package/docs-to-voice/scripts/docs_to_voice.sh +0 -11
  118. package/docs-to-voice/tests/test_docs_to_voice_api_max_chars.py +0 -210
  119. package/docs-to-voice/tests/test_docs_to_voice_sentence_timeline.py +0 -115
  120. package/docs-to-voice/tests/test_docs_to_voice_settings.py +0 -43
  121. package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +0 -51
  122. package/docs-to-voice/tests/test_docs_to_voice_speech_rate.py +0 -57
  123. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  124. package/generate-spec/scripts/create-specs +0 -215
  125. package/generate-spec/tests/test_create_specs.py +0 -200
  126. package/init-project-html/scripts/architecture-bootstrap-render.js +0 -16
  127. package/init-project-html/scripts/architecture.js +0 -296
  128. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  129. package/katex/scripts/render_katex.py +0 -247
  130. package/katex/scripts/render_katex.sh +0 -11
  131. package/katex/tests/test_render_katex.py +0 -174
  132. package/learning-error-book/scripts/render_error_book_json_to_pdf.py +0 -590
  133. package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +0 -134
  134. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  135. package/open-github-issue/scripts/open_github_issue.py +0 -705
  136. package/open-github-issue/tests/test_open_github_issue.py +0 -381
  137. package/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py +0 -763
  138. package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +0 -177
  139. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  140. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  141. package/read-github-issue/scripts/find_issues.py +0 -148
  142. package/read-github-issue/scripts/read_issue.py +0 -108
  143. package/read-github-issue/tests/test_find_issues.py +0 -127
  144. package/read-github-issue/tests/test_read_issue.py +0 -109
  145. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  146. package/resolve-review-comments/scripts/review_threads.py +0 -425
  147. package/resolve-review-comments/tests/test_review_threads.py +0 -74
  148. package/scripts/validate_openai_agent_config.py +0 -209
  149. package/scripts/validate_skill_frontmatter.py +0 -131
  150. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
  151. package/text-to-short-video/scripts/enforce_video_aspect_ratio.py +0 -350
  152. package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +0 -194
  153. package/weekly-financial-event-report/scripts/extract_pdf_text_pdfkit.swift +0 -99
  154. package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +0 -64
@@ -0,0 +1,262 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { createInterface } from 'node:readline/promises';
3
+ import { Readable } from 'node:stream';
4
+
5
+ const TIMESTAMP_PATTERN =
6
+ /(?<timestamp>\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[.,]\d+)?(?:Z|[+-]\d{2}:\d{2})?)/;
7
+
8
+ const TIMESTAMP_FORMATS = [
9
+ '%Y-%m-%dT%H:%M:%S.%f%z',
10
+ '%Y-%m-%dT%H:%M:%S%z',
11
+ '%Y-%m-%d %H:%M:%S.%f%z',
12
+ '%Y-%m-%d %H:%M:%S%z',
13
+ '%Y-%m-%dT%H:%M:%S.%f',
14
+ '%Y-%m-%dT%H:%M:%S',
15
+ '%Y-%m-%d %H:%M:%S.%f',
16
+ '%Y-%m-%d %H:%M:%S',
17
+ ];
18
+
19
+ /** Convert strftime-style format string to a regex and time-component extractors. */
20
+ function parseWithFormat(
21
+ value: string,
22
+ fmt: string,
23
+ assumeTimezone: string,
24
+ ): Date | null {
25
+ // Build a regex from the format string by replacing format specifiers
26
+ let regexStr = '';
27
+ let i = 0;
28
+ const groups: { name: string; width: number }[] = [];
29
+
30
+ while (i < fmt.length) {
31
+ if (fmt[i] === '%' && i + 1 < fmt.length) {
32
+ switch (fmt[i + 1]) {
33
+ case 'Y':
34
+ regexStr += '(\\d{4})';
35
+ groups.push({ name: 'year', width: 4 });
36
+ break;
37
+ case 'm':
38
+ regexStr += '(\\d{2})';
39
+ groups.push({ name: 'month', width: 2 });
40
+ break;
41
+ case 'd':
42
+ regexStr += '(\\d{2})';
43
+ groups.push({ name: 'day', width: 2 });
44
+ break;
45
+ case 'H':
46
+ regexStr += '(\\d{2})';
47
+ groups.push({ name: 'hour', width: 2 });
48
+ break;
49
+ case 'M':
50
+ regexStr += '(\\d{2})';
51
+ groups.push({ name: 'minute', width: 2 });
52
+ break;
53
+ case 'S':
54
+ regexStr += '(\\d{2})';
55
+ groups.push({ name: 'second', width: 2 });
56
+ break;
57
+ case 'f':
58
+ regexStr += '(\\d+)';
59
+ groups.push({ name: 'frac', width: 0 });
60
+ break;
61
+ case 'z':
62
+ regexStr += '([+-]\\d{2}:\\d{2})';
63
+ groups.push({ name: 'tz', width: 0 });
64
+ break;
65
+ default:
66
+ regexStr += '\\' + fmt[i + 1];
67
+ break;
68
+ }
69
+ i += 2;
70
+ } else {
71
+ regexStr += fmt[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
72
+ i++;
73
+ }
74
+ }
75
+
76
+ const match = value.match(new RegExp('^' + regexStr + '$'));
77
+ if (!match) return null;
78
+
79
+ const extract = (name: string): number => {
80
+ const idx = groups.findIndex((g) => g.name === name);
81
+ if (idx === -1) return 0;
82
+ return parseInt(match[idx + 1] || '0', 10);
83
+ };
84
+
85
+ const hasTimezone = groups.some((g) => g.name === 'tz');
86
+ const tzGroup = hasTimezone ? match[groups.findIndex((g) => g.name === 'tz') + 1] : null;
87
+ const fracRaw = groups.some((g) => g.name === 'frac')
88
+ ? match[groups.findIndex((g) => g.name === 'frac') + 1]
89
+ : null;
90
+
91
+ const year = extract('year');
92
+ const month = extract('month') - 1; // 0-indexed
93
+ const day = extract('day');
94
+ const hour = extract('hour');
95
+ const minute = extract('minute');
96
+ const second = extract('second');
97
+ let milliseconds = 0;
98
+ if (fracRaw != null) {
99
+ const padded = fracRaw.padEnd(3, '0').slice(0, 3);
100
+ milliseconds = parseInt(padded, 10);
101
+ }
102
+
103
+ if (tzGroup) {
104
+ const sign = tzGroup[0] === '-' ? -1 : 1;
105
+ const tzHours = parseInt(tzGroup.slice(1, 3), 10);
106
+ const tzMinutes = parseInt(tzGroup.slice(4, 6), 10);
107
+ const offsetMinutes = sign * (tzHours * 60 + tzMinutes);
108
+ const date = new Date(Date.UTC(year, month, day, hour, minute - offsetMinutes, second, milliseconds));
109
+ return date;
110
+ }
111
+
112
+ // No timezone in format -> assume timezone
113
+ return applyTimezone(
114
+ new Date(year, month, day, hour, minute, second, milliseconds),
115
+ assumeTimezone,
116
+ );
117
+ }
118
+
119
+ function applyTimezone(date: Date, assumeTimezone: string): Date {
120
+ const tzMinutes = parseTimezoneOffset(assumeTimezone);
121
+ const localMinutes = date.getTimezoneOffset();
122
+ // Convert from local to assumed timezone by adjusting offset difference
123
+ return new Date(date.getTime() + (localMinutes + tzMinutes) * 60 * 1000);
124
+ }
125
+
126
+ function parseTimezoneOffset(raw: string): number {
127
+ const upper = raw.toUpperCase();
128
+ if (upper === 'UTC' || upper === 'Z') return 0;
129
+
130
+ const match = /^([+-])(\d{2}):(\d{2})$/.exec(raw);
131
+ if (!match) throw new Error(`timezone must be UTC or ±HH:MM, got: ${raw}`);
132
+
133
+ const totalMinutes = parseInt(match[2], 10) * 60 + parseInt(match[3], 10);
134
+ return match[1] === '-' ? -totalMinutes : totalMinutes;
135
+ }
136
+
137
+ /**
138
+ * Normalize a raw timestamp string: strip whitespace, replace comma with dot,
139
+ * and convert trailing Z to +00:00.
140
+ */
141
+ export function normalizeTimestamp(raw: string): string {
142
+ const value = raw.trim().replace(',', '.');
143
+ if (value.endsWith('Z')) {
144
+ return value.slice(0, -1) + '+00:00';
145
+ }
146
+ return value;
147
+ }
148
+
149
+ /**
150
+ * Parse a CLI-provided timestamp string into a Date.
151
+ * Naive timestamps are assigned the assumeTimezone offset.
152
+ */
153
+ export function parseCliTimestamp(raw: string, assumeTimezone: string): Date {
154
+ const normalized = normalizeTimestamp(raw);
155
+ for (const fmt of TIMESTAMP_FORMATS) {
156
+ const parsed = parseWithFormat(normalized, fmt, assumeTimezone);
157
+ if (parsed) {
158
+ return parsed;
159
+ }
160
+ }
161
+ throw new Error(`invalid timestamp: ${raw}`);
162
+ }
163
+
164
+ /**
165
+ * Extract an ISO 8601 timestamp from a log line and parse it into a Date.
166
+ * Returns null if no timestamp is found or parsing fails.
167
+ */
168
+ export function extractTimestamp(
169
+ line: string,
170
+ assumeTimezone: string,
171
+ ): Date | null {
172
+ const match = TIMESTAMP_PATTERN.exec(line);
173
+ if (!match || !match.groups?.timestamp) return null;
174
+ try {
175
+ return parseCliTimestamp(match.groups.timestamp, assumeTimezone);
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Build a timezone offset in minutes from a timezone string.
183
+ * Supports "UTC" and "±HH:MM" formats.
184
+ */
185
+ export function buildTimezone(raw: string): number {
186
+ return parseTimezoneOffset(raw);
187
+ }
188
+
189
+ /**
190
+ * Validate that start is not later than end.
191
+ * Writes an error message to stderr and returns false if the order is inverted.
192
+ */
193
+ export function validateTimeWindow(
194
+ start: Date | null,
195
+ end: Date | null,
196
+ stderr: NodeJS.WriteStream,
197
+ ): boolean {
198
+ if (start && end && start > end) {
199
+ stderr.write('Error: --start must be earlier than or equal to --end.\n');
200
+ return false;
201
+ }
202
+ return true;
203
+ }
204
+
205
+ /**
206
+ * Check whether a timestamp falls within a given [start, end] window (inclusive).
207
+ * Returns false if timestamp is null.
208
+ */
209
+ export function inWindow(
210
+ timestamp: Date | null,
211
+ start: Date | null,
212
+ end: Date | null,
213
+ ): boolean {
214
+ if (timestamp === null) return false;
215
+ if (start !== null && timestamp < start) return false;
216
+ if (end !== null && timestamp > end) return false;
217
+ return true;
218
+ }
219
+
220
+ /**
221
+ * Iterate over lines from file paths or stdin.
222
+ * When paths is empty or contains "-", reads from stdin.
223
+ */
224
+ export async function* iterInputLines(
225
+ paths: string[],
226
+ ): AsyncGenerator<string> {
227
+ if (paths.length === 0) {
228
+ yield* readStdinLines();
229
+ return;
230
+ }
231
+
232
+ for (const rawPath of paths) {
233
+ if (rawPath === '-') {
234
+ yield* readStdinLines();
235
+ continue;
236
+ }
237
+ yield* readFileLines(rawPath);
238
+ }
239
+ }
240
+
241
+ async function* readStdinLines(): AsyncGenerator<string> {
242
+ const rl = createInterface({
243
+ input: process.stdin,
244
+ crlfDelay: Infinity,
245
+ });
246
+
247
+ for await (const line of rl) {
248
+ yield line;
249
+ }
250
+ }
251
+
252
+ async function* readFileLines(filePath: string): AsyncGenerator<string> {
253
+ const fileStream = createReadStream(filePath, { encoding: 'utf-8' });
254
+ const rl = createInterface({
255
+ input: fileStream,
256
+ crlfDelay: Infinity,
257
+ });
258
+
259
+ for await (const line of rl) {
260
+ yield line;
261
+ }
262
+ }