create-atsdc-stack 1.0.1 → 1.2.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 (48) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CLAUDE.md +236 -0
  3. package/CONTRIBUTING.md +342 -342
  4. package/INSTALLATION.md +359 -359
  5. package/LICENSE +201 -201
  6. package/README.md +405 -405
  7. package/app/.env.example +17 -17
  8. package/app/.github/labeler.yml +61 -0
  9. package/app/.github/workflows/browser-tests.yml +101 -0
  10. package/app/.github/workflows/check.yml +24 -0
  11. package/app/.github/workflows/greetings.yml +16 -0
  12. package/app/.github/workflows/label.yml +22 -0
  13. package/app/.github/workflows/stale.yml +27 -0
  14. package/app/.github/workflows/summary.yml +34 -0
  15. package/app/.stylelintrc.json +8 -0
  16. package/app/README.md +251 -251
  17. package/app/astro.config.mjs +83 -83
  18. package/app/drizzle.config.ts +16 -16
  19. package/app/package.json +66 -52
  20. package/app/playwright.config.ts +27 -0
  21. package/app/public/manifest.webmanifest +36 -36
  22. package/app/pwa-assets.config.ts +8 -0
  23. package/app/src/components/Card.astro +36 -36
  24. package/app/src/db/initialize.ts +107 -107
  25. package/app/src/db/schema.ts +72 -72
  26. package/app/src/db/validations.ts +158 -158
  27. package/app/src/layouts/Layout.astro +63 -63
  28. package/app/src/lib/config.ts +36 -36
  29. package/app/src/lib/content-converter.ts +141 -141
  30. package/app/src/lib/dom-utils.ts +230 -230
  31. package/app/src/lib/exa-search.ts +269 -269
  32. package/app/src/pages/api/chat.ts +91 -91
  33. package/app/src/pages/api/posts.ts +350 -350
  34. package/app/src/pages/index.astro +87 -87
  35. package/app/src/styles/components/button.scss +152 -152
  36. package/app/src/styles/components/card.scss +180 -180
  37. package/app/src/styles/components/form.scss +240 -240
  38. package/app/src/styles/global.scss +141 -141
  39. package/app/src/styles/pages/index.scss +80 -80
  40. package/app/src/styles/reset.scss +83 -83
  41. package/app/src/styles/variables/globals.scss +96 -96
  42. package/app/src/styles/variables/mixins.scss +238 -238
  43. package/app/tests/browser.test.nopause.ts +10 -0
  44. package/app/tests/browser.test.ts +13 -0
  45. package/bin/cli.js +1151 -1138
  46. package/package.json +8 -6
  47. package/app/.astro/settings.json +0 -5
  48. package/app/.astro/types.d.ts +0 -1
@@ -1,269 +1,269 @@
1
- /**
2
- * Exa Search Integration
3
- * Provides AI-powered search capabilities using Exa
4
- * Documentation: https://docs.exa.ai
5
- */
6
-
7
- import Exa from 'exa-js';
8
-
9
- /**
10
- * Initialize Exa client
11
- * Requires EXA_API_KEY environment variable
12
- */
13
- function getExaClient(): Exa {
14
- const apiKey = import.meta.env.EXA_API_KEY || process.env.EXA_API_KEY;
15
-
16
- if (!apiKey) {
17
- throw new Error('EXA_API_KEY environment variable is not set');
18
- }
19
-
20
- return new Exa(apiKey);
21
- }
22
-
23
- /**
24
- * Search options for Exa
25
- */
26
- export interface ExaSearchOptions {
27
- /** Number of results to return (1-10, default: 10) */
28
- numResults?: number;
29
-
30
- /** Type of search: 'neural', 'keyword', or 'auto' (default: 'auto') */
31
- type?: 'neural' | 'keyword' | 'auto';
32
-
33
- /** Whether to use autoprompt to expand query (default: false) */
34
- useAutoprompt?: boolean;
35
-
36
- /** Category of content to search */
37
- category?: 'company' | 'research paper' | 'news' | 'github' | 'tweet' | 'movie' | 'song' | 'personal site' | 'pdf';
38
-
39
- /** Start crawl date (YYYY-MM-DD) */
40
- startCrawlDate?: string;
41
-
42
- /** End crawl date (YYYY-MM-DD) */
43
- endCrawlDate?: string;
44
-
45
- /** Start published date (YYYY-MM-DD) */
46
- startPublishedDate?: string;
47
-
48
- /** End published date (YYYY-MM-DD) */
49
- endPublishedDate?: string;
50
-
51
- /** Include domains (whitelist) */
52
- includeDomains?: string[];
53
-
54
- /** Exclude domains (blacklist) */
55
- excludeDomains?: string[];
56
-
57
- /** Whether to include page content */
58
- contents?: {
59
- text?: boolean | { maxCharacters?: number };
60
- highlights?: boolean | { numSentences?: number; highlightsPerUrl?: number };
61
- summary?: boolean | { query?: string };
62
- };
63
- }
64
-
65
- /**
66
- * Exa search result
67
- */
68
- export interface ExaResult {
69
- url: string;
70
- title: string;
71
- publishedDate?: string;
72
- author?: string;
73
- score?: number;
74
- id: string;
75
- text?: string;
76
- highlights?: string[];
77
- summary?: string;
78
- }
79
-
80
- /**
81
- * Search the web using Exa
82
- * @param query - The search query
83
- * @param options - Search options
84
- * @returns Array of search results
85
- */
86
- export async function searchWeb(
87
- query: string,
88
- options: ExaSearchOptions = {}
89
- ): Promise<ExaResult[]> {
90
- try {
91
- const exa = getExaClient();
92
-
93
- const response = await exa.searchAndContents(query, {
94
- numResults: options.numResults || 10,
95
- type: options.type || 'auto',
96
- useAutoprompt: options.useAutoprompt || false,
97
- category: options.category,
98
- startCrawlDate: options.startCrawlDate,
99
- endCrawlDate: options.endCrawlDate,
100
- startPublishedDate: options.startPublishedDate,
101
- endPublishedDate: options.endPublishedDate,
102
- includeDomains: options.includeDomains,
103
- excludeDomains: options.excludeDomains,
104
- text: options.contents?.text,
105
- highlights: options.contents?.highlights,
106
- summary: options.contents?.summary,
107
- });
108
-
109
- return response.results.map((result: any) => ({
110
- url: result.url,
111
- title: result.title,
112
- publishedDate: result.publishedDate,
113
- author: result.author,
114
- score: result.score,
115
- id: result.id,
116
- text: result.text,
117
- highlights: result.highlights,
118
- summary: result.summary,
119
- }));
120
- } catch (error) {
121
- console.error('Exa search error:', error);
122
- throw new Error('Failed to perform search');
123
- }
124
- }
125
-
126
- /**
127
- * Find similar content to a given URL
128
- * @param url - The URL to find similar content for
129
- * @param options - Search options
130
- * @returns Array of similar results
131
- */
132
- export async function findSimilar(
133
- url: string,
134
- options: Omit<ExaSearchOptions, 'type' | 'useAutoprompt'> = {}
135
- ): Promise<ExaResult[]> {
136
- try {
137
- const exa = getExaClient();
138
-
139
- const response = await exa.findSimilarAndContents(url, {
140
- numResults: options.numResults || 10,
141
- category: options.category,
142
- startCrawlDate: options.startCrawlDate,
143
- endCrawlDate: options.endCrawlDate,
144
- startPublishedDate: options.startPublishedDate,
145
- endPublishedDate: options.endPublishedDate,
146
- includeDomains: options.includeDomains,
147
- excludeDomains: options.excludeDomains,
148
- text: options.contents?.text,
149
- highlights: options.contents?.highlights,
150
- summary: options.contents?.summary,
151
- });
152
-
153
- return response.results.map((result: any) => ({
154
- url: result.url,
155
- title: result.title,
156
- publishedDate: result.publishedDate,
157
- author: result.author,
158
- score: result.score,
159
- id: result.id,
160
- text: result.text,
161
- highlights: result.highlights,
162
- summary: result.summary,
163
- }));
164
- } catch (error) {
165
- console.error('Exa find similar error:', error);
166
- throw new Error('Failed to find similar content');
167
- }
168
- }
169
-
170
- /**
171
- * Get full page content for a list of URLs
172
- * @param urls - Array of URLs to get content for
173
- * @param options - Content options
174
- * @returns Array of results with content
175
- */
176
- export async function getContents(
177
- urls: string[],
178
- options: ExaSearchOptions['contents'] = {}
179
- ): Promise<ExaResult[]> {
180
- try {
181
- const exa = getExaClient();
182
-
183
- const response = await exa.getContents(urls, {
184
- text: options.text,
185
- highlights: options.highlights,
186
- summary: options.summary,
187
- });
188
-
189
- return response.results.map((result: any) => ({
190
- url: result.url,
191
- title: result.title,
192
- publishedDate: result.publishedDate,
193
- author: result.author,
194
- id: result.id,
195
- text: result.text,
196
- highlights: result.highlights,
197
- summary: result.summary,
198
- }));
199
- } catch (error) {
200
- console.error('Exa get contents error:', error);
201
- throw new Error('Failed to get contents');
202
- }
203
- }
204
-
205
- /**
206
- * Search for recent news articles
207
- * @param query - The search query
208
- * @param daysBack - Number of days to look back (default: 7)
209
- * @returns Array of news results
210
- */
211
- export async function searchNews(query: string, daysBack: number = 7): Promise<ExaResult[]> {
212
- const endDate = new Date();
213
- const startDate = new Date();
214
- startDate.setDate(startDate.getDate() - daysBack);
215
-
216
- return searchWeb(query, {
217
- category: 'news',
218
- startPublishedDate: startDate.toISOString().split('T')[0],
219
- endPublishedDate: endDate.toISOString().split('T')[0],
220
- numResults: 10,
221
- contents: {
222
- text: { maxCharacters: 1000 },
223
- highlights: { numSentences: 3 },
224
- },
225
- });
226
- }
227
-
228
- /**
229
- * Search for research papers
230
- * @param query - The search query
231
- * @param options - Additional search options
232
- * @returns Array of research paper results
233
- */
234
- export async function searchResearch(
235
- query: string,
236
- options: ExaSearchOptions = {}
237
- ): Promise<ExaResult[]> {
238
- return searchWeb(query, {
239
- ...options,
240
- category: 'research paper',
241
- contents: {
242
- text: { maxCharacters: 2000 },
243
- summary: true,
244
- ...options.contents,
245
- },
246
- });
247
- }
248
-
249
- /**
250
- * Search GitHub repositories
251
- * @param query - The search query
252
- * @param options - Additional search options
253
- * @returns Array of GitHub results
254
- */
255
- export async function searchGitHub(
256
- query: string,
257
- options: ExaSearchOptions = {}
258
- ): Promise<ExaResult[]> {
259
- return searchWeb(query, {
260
- ...options,
261
- category: 'github',
262
- includeDomains: ['github.com'],
263
- contents: {
264
- text: { maxCharacters: 1500 },
265
- highlights: { numSentences: 5 },
266
- ...options.contents,
267
- },
268
- });
269
- }
1
+ /**
2
+ * Exa Search Integration
3
+ * Provides AI-powered search capabilities using Exa
4
+ * Documentation: https://docs.exa.ai
5
+ */
6
+
7
+ import Exa from 'exa-js';
8
+
9
+ /**
10
+ * Initialize Exa client
11
+ * Requires EXA_API_KEY environment variable
12
+ */
13
+ function getExaClient(): Exa {
14
+ const apiKey = import.meta.env.EXA_API_KEY || process.env.EXA_API_KEY;
15
+
16
+ if (!apiKey) {
17
+ throw new Error('EXA_API_KEY environment variable is not set');
18
+ }
19
+
20
+ return new Exa(apiKey);
21
+ }
22
+
23
+ /**
24
+ * Search options for Exa
25
+ */
26
+ export interface ExaSearchOptions {
27
+ /** Number of results to return (1-10, default: 10) */
28
+ numResults?: number;
29
+
30
+ /** Type of search: 'neural', 'keyword', or 'auto' (default: 'auto') */
31
+ type?: 'neural' | 'keyword' | 'auto';
32
+
33
+ /** Whether to use autoprompt to expand query (default: false) */
34
+ useAutoprompt?: boolean;
35
+
36
+ /** Category of content to search */
37
+ category?: 'company' | 'research paper' | 'news' | 'github' | 'tweet' | 'movie' | 'song' | 'personal site' | 'pdf';
38
+
39
+ /** Start crawl date (YYYY-MM-DD) */
40
+ startCrawlDate?: string;
41
+
42
+ /** End crawl date (YYYY-MM-DD) */
43
+ endCrawlDate?: string;
44
+
45
+ /** Start published date (YYYY-MM-DD) */
46
+ startPublishedDate?: string;
47
+
48
+ /** End published date (YYYY-MM-DD) */
49
+ endPublishedDate?: string;
50
+
51
+ /** Include domains (whitelist) */
52
+ includeDomains?: string[];
53
+
54
+ /** Exclude domains (blacklist) */
55
+ excludeDomains?: string[];
56
+
57
+ /** Whether to include page content */
58
+ contents?: {
59
+ text?: boolean | { maxCharacters?: number };
60
+ highlights?: boolean | { numSentences?: number; highlightsPerUrl?: number };
61
+ summary?: boolean | { query?: string };
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Exa search result
67
+ */
68
+ export interface ExaResult {
69
+ url: string;
70
+ title: string;
71
+ publishedDate?: string;
72
+ author?: string;
73
+ score?: number;
74
+ id: string;
75
+ text?: string;
76
+ highlights?: string[];
77
+ summary?: string;
78
+ }
79
+
80
+ /**
81
+ * Search the web using Exa
82
+ * @param query - The search query
83
+ * @param options - Search options
84
+ * @returns Array of search results
85
+ */
86
+ export async function searchWeb(
87
+ query: string,
88
+ options: ExaSearchOptions = {}
89
+ ): Promise<ExaResult[]> {
90
+ try {
91
+ const exa = getExaClient();
92
+
93
+ const response = await exa.searchAndContents(query, {
94
+ numResults: options.numResults || 10,
95
+ type: options.type || 'auto',
96
+ useAutoprompt: options.useAutoprompt || false,
97
+ category: options.category,
98
+ startCrawlDate: options.startCrawlDate,
99
+ endCrawlDate: options.endCrawlDate,
100
+ startPublishedDate: options.startPublishedDate,
101
+ endPublishedDate: options.endPublishedDate,
102
+ includeDomains: options.includeDomains,
103
+ excludeDomains: options.excludeDomains,
104
+ text: options.contents?.text,
105
+ highlights: options.contents?.highlights,
106
+ summary: options.contents?.summary,
107
+ });
108
+
109
+ return response.results.map((result: any) => ({
110
+ url: result.url,
111
+ title: result.title,
112
+ publishedDate: result.publishedDate,
113
+ author: result.author,
114
+ score: result.score,
115
+ id: result.id,
116
+ text: result.text,
117
+ highlights: result.highlights,
118
+ summary: result.summary,
119
+ }));
120
+ } catch (error) {
121
+ console.error('Exa search error:', error);
122
+ throw new Error('Failed to perform search');
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Find similar content to a given URL
128
+ * @param url - The URL to find similar content for
129
+ * @param options - Search options
130
+ * @returns Array of similar results
131
+ */
132
+ export async function findSimilar(
133
+ url: string,
134
+ options: Omit<ExaSearchOptions, 'type' | 'useAutoprompt'> = {}
135
+ ): Promise<ExaResult[]> {
136
+ try {
137
+ const exa = getExaClient();
138
+
139
+ const response = await exa.findSimilarAndContents(url, {
140
+ numResults: options.numResults || 10,
141
+ category: options.category,
142
+ startCrawlDate: options.startCrawlDate,
143
+ endCrawlDate: options.endCrawlDate,
144
+ startPublishedDate: options.startPublishedDate,
145
+ endPublishedDate: options.endPublishedDate,
146
+ includeDomains: options.includeDomains,
147
+ excludeDomains: options.excludeDomains,
148
+ text: options.contents?.text,
149
+ highlights: options.contents?.highlights,
150
+ summary: options.contents?.summary,
151
+ });
152
+
153
+ return response.results.map((result: any) => ({
154
+ url: result.url,
155
+ title: result.title,
156
+ publishedDate: result.publishedDate,
157
+ author: result.author,
158
+ score: result.score,
159
+ id: result.id,
160
+ text: result.text,
161
+ highlights: result.highlights,
162
+ summary: result.summary,
163
+ }));
164
+ } catch (error) {
165
+ console.error('Exa find similar error:', error);
166
+ throw new Error('Failed to find similar content');
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Get full page content for a list of URLs
172
+ * @param urls - Array of URLs to get content for
173
+ * @param options - Content options
174
+ * @returns Array of results with content
175
+ */
176
+ export async function getContents(
177
+ urls: string[],
178
+ options: ExaSearchOptions['contents'] = {}
179
+ ): Promise<ExaResult[]> {
180
+ try {
181
+ const exa = getExaClient();
182
+
183
+ const response = await exa.getContents(urls, {
184
+ text: options.text,
185
+ highlights: options.highlights,
186
+ summary: options.summary,
187
+ });
188
+
189
+ return response.results.map((result: any) => ({
190
+ url: result.url,
191
+ title: result.title,
192
+ publishedDate: result.publishedDate,
193
+ author: result.author,
194
+ id: result.id,
195
+ text: result.text,
196
+ highlights: result.highlights,
197
+ summary: result.summary,
198
+ }));
199
+ } catch (error) {
200
+ console.error('Exa get contents error:', error);
201
+ throw new Error('Failed to get contents');
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Search for recent news articles
207
+ * @param query - The search query
208
+ * @param daysBack - Number of days to look back (default: 7)
209
+ * @returns Array of news results
210
+ */
211
+ export async function searchNews(query: string, daysBack: number = 7): Promise<ExaResult[]> {
212
+ const endDate = new Date();
213
+ const startDate = new Date();
214
+ startDate.setDate(startDate.getDate() - daysBack);
215
+
216
+ return searchWeb(query, {
217
+ category: 'news',
218
+ startPublishedDate: startDate.toISOString().split('T')[0],
219
+ endPublishedDate: endDate.toISOString().split('T')[0],
220
+ numResults: 10,
221
+ contents: {
222
+ text: { maxCharacters: 1000 },
223
+ highlights: { numSentences: 3 },
224
+ },
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Search for research papers
230
+ * @param query - The search query
231
+ * @param options - Additional search options
232
+ * @returns Array of research paper results
233
+ */
234
+ export async function searchResearch(
235
+ query: string,
236
+ options: ExaSearchOptions = {}
237
+ ): Promise<ExaResult[]> {
238
+ return searchWeb(query, {
239
+ ...options,
240
+ category: 'research paper',
241
+ contents: {
242
+ text: { maxCharacters: 2000 },
243
+ summary: true,
244
+ ...options.contents,
245
+ },
246
+ });
247
+ }
248
+
249
+ /**
250
+ * Search GitHub repositories
251
+ * @param query - The search query
252
+ * @param options - Additional search options
253
+ * @returns Array of GitHub results
254
+ */
255
+ export async function searchGitHub(
256
+ query: string,
257
+ options: ExaSearchOptions = {}
258
+ ): Promise<ExaResult[]> {
259
+ return searchWeb(query, {
260
+ ...options,
261
+ category: 'github',
262
+ includeDomains: ['github.com'],
263
+ contents: {
264
+ text: { maxCharacters: 1500 },
265
+ highlights: { numSentences: 5 },
266
+ ...options.contents,
267
+ },
268
+ });
269
+ }