design-system-selector 0.1.1 → 0.1.2
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 +16 -0
- package/dist/cli.js +50 -120
- package/dist/index.js +50 -120
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -18,6 +18,22 @@ brew install design-system-selector
|
|
|
18
18
|
|
|
19
19
|
Requires Node.js 20 or later.
|
|
20
20
|
|
|
21
|
+
## AI Matching
|
|
22
|
+
|
|
23
|
+
AI matching is provided through the hosted Design System Selector API proxy.
|
|
24
|
+
|
|
25
|
+
- Users do **not** need to configure `ANTHROPIC_API_KEY`
|
|
26
|
+
- The CLI sends questionnaire preferences to the backend and receives ranked results
|
|
27
|
+
- If the API is unavailable, the CLI automatically falls back to static matching
|
|
28
|
+
|
|
29
|
+
For local backend testing, maintainers can override the API base URL:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
DESIGN_SYSTEM_SELECTOR_API_URL=http://localhost:8787 npx design-system-selector
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Backend deployment guide: [`docs/vercel-backend.md`](docs/vercel-backend.md)
|
|
36
|
+
|
|
21
37
|
## How It Works
|
|
22
38
|
|
|
23
39
|
The CLI asks you 5 questions about your project:
|
package/dist/cli.js
CHANGED
|
@@ -402,91 +402,43 @@ function match(systems2, prefs) {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
// src/agent/client.ts
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
);
|
|
405
|
+
var DEFAULT_AGENT_API_URL = "https://api.thelostbygass.com";
|
|
406
|
+
var AGENT_MATCH_PATH = "/v1/match";
|
|
407
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
408
|
+
function normalizeBaseUrl(raw) {
|
|
409
|
+
return raw.replace(/\/+$/, "");
|
|
410
|
+
}
|
|
411
|
+
function getAgentApiUrl() {
|
|
412
|
+
const configured = process.env["DESIGN_SYSTEM_SELECTOR_API_URL"];
|
|
413
|
+
if (typeof configured === "string" && configured.trim().length > 0) {
|
|
414
|
+
return normalizeBaseUrl(configured.trim());
|
|
415
|
+
}
|
|
416
|
+
return DEFAULT_AGENT_API_URL;
|
|
417
|
+
}
|
|
418
|
+
function buildEndpoint() {
|
|
419
|
+
return `${getAgentApiUrl()}${AGENT_MATCH_PATH}`;
|
|
420
|
+
}
|
|
421
|
+
async function requestAgentMatch(preferences) {
|
|
422
|
+
const payload = { preferences };
|
|
423
|
+
const response = await fetch(buildEndpoint(), {
|
|
424
|
+
method: "POST",
|
|
425
|
+
headers: {
|
|
426
|
+
"Content-Type": "application/json",
|
|
427
|
+
Accept: "application/json"
|
|
428
|
+
},
|
|
429
|
+
body: JSON.stringify(payload),
|
|
430
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
431
|
+
});
|
|
432
|
+
if (!response.ok) {
|
|
433
|
+
const details = (await response.text()).trim();
|
|
434
|
+
const suffix = details ? `: ${details}` : "";
|
|
435
|
+
throw new Error(`AI API request failed (${response.status})${suffix}`);
|
|
415
436
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return `You are a design system advisor. Given a set of design systems and user preferences, score and rank the systems to find the best matches.
|
|
422
|
-
|
|
423
|
-
## Scoring Dimensions (total 100 points)
|
|
424
|
-
- accessibility (22): Match accessibility score and EU compliance to user need
|
|
425
|
-
- designPhilosophy (18): Match visual style to system philosophy
|
|
426
|
-
- customization (18): Match opinion level to customization depth
|
|
427
|
-
- learningCurve (17): Match experience level to learning curve
|
|
428
|
-
- projectType (15): Match project type to system strengths
|
|
429
|
-
- color (10): Match color preference to system color strengths
|
|
430
|
-
|
|
431
|
-
## Accessibility Hard Filter
|
|
432
|
-
Before scoring, exclude systems that fail the accessibility filter:
|
|
433
|
-
- "critical" need \u2192 ONLY systems with accessibilityScore "AAA" AND euCompliance "full"
|
|
434
|
-
- "important" need \u2192 ONLY systems with accessibilityScore "AAA" or "AA"
|
|
435
|
-
- "standard" need \u2192 all systems pass
|
|
436
|
-
|
|
437
|
-
## Framework Hard Filter
|
|
438
|
-
Before scoring, exclude systems that don't support the user's framework:
|
|
439
|
-
- If framework is "any" \u2192 all systems pass
|
|
440
|
-
- Otherwise \u2192 ONLY systems whose frameworks array includes the user's framework
|
|
441
|
-
|
|
442
|
-
## Output Format
|
|
443
|
-
Return ONLY valid JSON (no markdown, no explanation) matching this exact shape:
|
|
444
|
-
{
|
|
445
|
-
"primary": { "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] },
|
|
446
|
-
"alternatives": [
|
|
447
|
-
{ "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] },
|
|
448
|
-
{ "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] }
|
|
449
|
-
],
|
|
450
|
-
"relaxed": boolean
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
Rules:
|
|
454
|
-
- primary is the best match, alternatives are the next two best
|
|
455
|
-
- reasons should be 2-4 concise, context-aware explanations per system
|
|
456
|
-
- relaxed is true if primary matchedDimensions < 4
|
|
457
|
-
- systemId must be one of the provided system IDs
|
|
458
|
-
- score is 0-100, matchedDimensions is 0-6`;
|
|
459
|
-
}
|
|
460
|
-
function compressSystem(system) {
|
|
461
|
-
return {
|
|
462
|
-
id: system.id,
|
|
463
|
-
name: system.name,
|
|
464
|
-
philosophy: system.designPhilosophy.join(","),
|
|
465
|
-
frameworks: system.frameworks.join(","),
|
|
466
|
-
a11y: system.accessibilityScore,
|
|
467
|
-
customization: system.customizationDepth,
|
|
468
|
-
learning: system.learningCurve,
|
|
469
|
-
colors: system.colorStrengths.join(","),
|
|
470
|
-
projects: system.projectStrengths.join(","),
|
|
471
|
-
eu: system.euCompliance,
|
|
472
|
-
tradeoffs: system.tradeoffs
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function buildUserMessage(systems2, prefs) {
|
|
476
|
-
const compressed = systems2.map(compressSystem);
|
|
477
|
-
return `## Available Design Systems
|
|
478
|
-
${compressed.map((s2) => `- ${s2.id}: ${s2.name} | philosophy=${s2.philosophy} | frameworks=${s2.frameworks} | a11y=${s2.a11y} | eu=${s2.eu} | customization=${s2.customization} | learning=${s2.learning} | colors=${s2.colors} | projects=${s2.projects} | tradeoffs="${s2.tradeoffs}"`).join("\n")}
|
|
479
|
-
|
|
480
|
-
## User Preferences
|
|
481
|
-
- visualStyle: ${prefs.visualStyle}
|
|
482
|
-
- accessibilityNeed: ${prefs.accessibilityNeed}
|
|
483
|
-
- opinionLevel: ${prefs.opinionLevel}
|
|
484
|
-
- experienceLevel: ${prefs.experienceLevel}
|
|
485
|
-
- colorPreference: ${prefs.colorPreference}
|
|
486
|
-
- projectType: ${prefs.projectType}
|
|
487
|
-
- framework: ${prefs.framework}
|
|
488
|
-
|
|
489
|
-
Score and rank these systems for this user. Return JSON only.`;
|
|
437
|
+
const json = await response.json();
|
|
438
|
+
if (typeof json !== "object" || json === null) {
|
|
439
|
+
throw new Error("AI API response is not a JSON object");
|
|
440
|
+
}
|
|
441
|
+
return json;
|
|
490
442
|
}
|
|
491
443
|
|
|
492
444
|
// src/agent/parser.ts
|
|
@@ -581,24 +533,8 @@ function parseAgentResponse(raw, systems2) {
|
|
|
581
533
|
|
|
582
534
|
// src/agent/matcher.ts
|
|
583
535
|
async function agentMatch(systems2, prefs) {
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
model: "claude-haiku-4-5-20251001",
|
|
587
|
-
max_tokens: 1024,
|
|
588
|
-
temperature: 0,
|
|
589
|
-
system: buildSystemPrompt(),
|
|
590
|
-
messages: [
|
|
591
|
-
{
|
|
592
|
-
role: "user",
|
|
593
|
-
content: buildUserMessage(systems2, prefs)
|
|
594
|
-
}
|
|
595
|
-
]
|
|
596
|
-
});
|
|
597
|
-
const textBlock = response.content.find((block) => block.type === "text");
|
|
598
|
-
if (!textBlock || textBlock.type !== "text") {
|
|
599
|
-
throw new Error("No text content in AI response");
|
|
600
|
-
}
|
|
601
|
-
return parseAgentResponse(textBlock.text, systems2);
|
|
536
|
+
const rawResponse = await requestAgentMatch(prefs);
|
|
537
|
+
return parseAgentResponse(JSON.stringify(rawResponse), systems2);
|
|
602
538
|
}
|
|
603
539
|
|
|
604
540
|
// src/output.ts
|
|
@@ -3773,26 +3709,20 @@ async function main() {
|
|
|
3773
3709
|
}
|
|
3774
3710
|
const prefs = await runQuestionnaire();
|
|
3775
3711
|
let result;
|
|
3776
|
-
if (
|
|
3712
|
+
if (!mcpOnly) {
|
|
3713
|
+
console.log(chalk5.dim("\n Analyzing with AI...\n"));
|
|
3714
|
+
}
|
|
3715
|
+
try {
|
|
3716
|
+
result = await agentMatch(systems, prefs);
|
|
3717
|
+
} catch (err) {
|
|
3777
3718
|
if (!mcpOnly) {
|
|
3778
|
-
console.log(
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
result = await agentMatch(systems, prefs);
|
|
3782
|
-
} catch (err) {
|
|
3783
|
-
if (!mcpOnly) {
|
|
3784
|
-
console.log(
|
|
3785
|
-
chalk5.yellow(
|
|
3786
|
-
` AI matching failed (${err instanceof Error ? err.message : "unknown error"}), using static analysis...
|
|
3719
|
+
console.log(
|
|
3720
|
+
chalk5.yellow(
|
|
3721
|
+
` AI matching unavailable (${err instanceof Error ? err.message : "unknown error"}), using static analysis...
|
|
3787
3722
|
`
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
result = match(systems, prefs);
|
|
3792
|
-
}
|
|
3793
|
-
} else {
|
|
3794
|
-
if (!mcpOnly) {
|
|
3795
|
-
console.log(chalk5.dim("\n Analyzing your preferences...\n"));
|
|
3723
|
+
)
|
|
3724
|
+
);
|
|
3725
|
+
console.log(chalk5.dim(" Analyzing your preferences...\n"));
|
|
3796
3726
|
}
|
|
3797
3727
|
result = match(systems, prefs);
|
|
3798
3728
|
}
|
package/dist/index.js
CHANGED
|
@@ -400,91 +400,43 @@ function match(systems2, prefs) {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
// src/agent/client.ts
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
);
|
|
403
|
+
var DEFAULT_AGENT_API_URL = "https://api.thelostbygass.com";
|
|
404
|
+
var AGENT_MATCH_PATH = "/v1/match";
|
|
405
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
406
|
+
function normalizeBaseUrl(raw) {
|
|
407
|
+
return raw.replace(/\/+$/, "");
|
|
408
|
+
}
|
|
409
|
+
function getAgentApiUrl() {
|
|
410
|
+
const configured = process.env["DESIGN_SYSTEM_SELECTOR_API_URL"];
|
|
411
|
+
if (typeof configured === "string" && configured.trim().length > 0) {
|
|
412
|
+
return normalizeBaseUrl(configured.trim());
|
|
413
|
+
}
|
|
414
|
+
return DEFAULT_AGENT_API_URL;
|
|
415
|
+
}
|
|
416
|
+
function buildEndpoint() {
|
|
417
|
+
return `${getAgentApiUrl()}${AGENT_MATCH_PATH}`;
|
|
418
|
+
}
|
|
419
|
+
async function requestAgentMatch(preferences) {
|
|
420
|
+
const payload = { preferences };
|
|
421
|
+
const response = await fetch(buildEndpoint(), {
|
|
422
|
+
method: "POST",
|
|
423
|
+
headers: {
|
|
424
|
+
"Content-Type": "application/json",
|
|
425
|
+
Accept: "application/json"
|
|
426
|
+
},
|
|
427
|
+
body: JSON.stringify(payload),
|
|
428
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
429
|
+
});
|
|
430
|
+
if (!response.ok) {
|
|
431
|
+
const details = (await response.text()).trim();
|
|
432
|
+
const suffix = details ? `: ${details}` : "";
|
|
433
|
+
throw new Error(`AI API request failed (${response.status})${suffix}`);
|
|
413
434
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
return `You are a design system advisor. Given a set of design systems and user preferences, score and rank the systems to find the best matches.
|
|
420
|
-
|
|
421
|
-
## Scoring Dimensions (total 100 points)
|
|
422
|
-
- accessibility (22): Match accessibility score and EU compliance to user need
|
|
423
|
-
- designPhilosophy (18): Match visual style to system philosophy
|
|
424
|
-
- customization (18): Match opinion level to customization depth
|
|
425
|
-
- learningCurve (17): Match experience level to learning curve
|
|
426
|
-
- projectType (15): Match project type to system strengths
|
|
427
|
-
- color (10): Match color preference to system color strengths
|
|
428
|
-
|
|
429
|
-
## Accessibility Hard Filter
|
|
430
|
-
Before scoring, exclude systems that fail the accessibility filter:
|
|
431
|
-
- "critical" need \u2192 ONLY systems with accessibilityScore "AAA" AND euCompliance "full"
|
|
432
|
-
- "important" need \u2192 ONLY systems with accessibilityScore "AAA" or "AA"
|
|
433
|
-
- "standard" need \u2192 all systems pass
|
|
434
|
-
|
|
435
|
-
## Framework Hard Filter
|
|
436
|
-
Before scoring, exclude systems that don't support the user's framework:
|
|
437
|
-
- If framework is "any" \u2192 all systems pass
|
|
438
|
-
- Otherwise \u2192 ONLY systems whose frameworks array includes the user's framework
|
|
439
|
-
|
|
440
|
-
## Output Format
|
|
441
|
-
Return ONLY valid JSON (no markdown, no explanation) matching this exact shape:
|
|
442
|
-
{
|
|
443
|
-
"primary": { "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] },
|
|
444
|
-
"alternatives": [
|
|
445
|
-
{ "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] },
|
|
446
|
-
{ "systemId": string, "score": number (0-100), "matchedDimensions": number (0-6), "reasons": string[] }
|
|
447
|
-
],
|
|
448
|
-
"relaxed": boolean
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
Rules:
|
|
452
|
-
- primary is the best match, alternatives are the next two best
|
|
453
|
-
- reasons should be 2-4 concise, context-aware explanations per system
|
|
454
|
-
- relaxed is true if primary matchedDimensions < 4
|
|
455
|
-
- systemId must be one of the provided system IDs
|
|
456
|
-
- score is 0-100, matchedDimensions is 0-6`;
|
|
457
|
-
}
|
|
458
|
-
function compressSystem(system) {
|
|
459
|
-
return {
|
|
460
|
-
id: system.id,
|
|
461
|
-
name: system.name,
|
|
462
|
-
philosophy: system.designPhilosophy.join(","),
|
|
463
|
-
frameworks: system.frameworks.join(","),
|
|
464
|
-
a11y: system.accessibilityScore,
|
|
465
|
-
customization: system.customizationDepth,
|
|
466
|
-
learning: system.learningCurve,
|
|
467
|
-
colors: system.colorStrengths.join(","),
|
|
468
|
-
projects: system.projectStrengths.join(","),
|
|
469
|
-
eu: system.euCompliance,
|
|
470
|
-
tradeoffs: system.tradeoffs
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function buildUserMessage(systems2, prefs) {
|
|
474
|
-
const compressed = systems2.map(compressSystem);
|
|
475
|
-
return `## Available Design Systems
|
|
476
|
-
${compressed.map((s2) => `- ${s2.id}: ${s2.name} | philosophy=${s2.philosophy} | frameworks=${s2.frameworks} | a11y=${s2.a11y} | eu=${s2.eu} | customization=${s2.customization} | learning=${s2.learning} | colors=${s2.colors} | projects=${s2.projects} | tradeoffs="${s2.tradeoffs}"`).join("\n")}
|
|
477
|
-
|
|
478
|
-
## User Preferences
|
|
479
|
-
- visualStyle: ${prefs.visualStyle}
|
|
480
|
-
- accessibilityNeed: ${prefs.accessibilityNeed}
|
|
481
|
-
- opinionLevel: ${prefs.opinionLevel}
|
|
482
|
-
- experienceLevel: ${prefs.experienceLevel}
|
|
483
|
-
- colorPreference: ${prefs.colorPreference}
|
|
484
|
-
- projectType: ${prefs.projectType}
|
|
485
|
-
- framework: ${prefs.framework}
|
|
486
|
-
|
|
487
|
-
Score and rank these systems for this user. Return JSON only.`;
|
|
435
|
+
const json = await response.json();
|
|
436
|
+
if (typeof json !== "object" || json === null) {
|
|
437
|
+
throw new Error("AI API response is not a JSON object");
|
|
438
|
+
}
|
|
439
|
+
return json;
|
|
488
440
|
}
|
|
489
441
|
|
|
490
442
|
// src/agent/parser.ts
|
|
@@ -579,24 +531,8 @@ function parseAgentResponse(raw, systems2) {
|
|
|
579
531
|
|
|
580
532
|
// src/agent/matcher.ts
|
|
581
533
|
async function agentMatch(systems2, prefs) {
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
model: "claude-haiku-4-5-20251001",
|
|
585
|
-
max_tokens: 1024,
|
|
586
|
-
temperature: 0,
|
|
587
|
-
system: buildSystemPrompt(),
|
|
588
|
-
messages: [
|
|
589
|
-
{
|
|
590
|
-
role: "user",
|
|
591
|
-
content: buildUserMessage(systems2, prefs)
|
|
592
|
-
}
|
|
593
|
-
]
|
|
594
|
-
});
|
|
595
|
-
const textBlock = response.content.find((block) => block.type === "text");
|
|
596
|
-
if (!textBlock || textBlock.type !== "text") {
|
|
597
|
-
throw new Error("No text content in AI response");
|
|
598
|
-
}
|
|
599
|
-
return parseAgentResponse(textBlock.text, systems2);
|
|
534
|
+
const rawResponse = await requestAgentMatch(prefs);
|
|
535
|
+
return parseAgentResponse(JSON.stringify(rawResponse), systems2);
|
|
600
536
|
}
|
|
601
537
|
|
|
602
538
|
// src/output.ts
|
|
@@ -3771,26 +3707,20 @@ async function main() {
|
|
|
3771
3707
|
}
|
|
3772
3708
|
const prefs = await runQuestionnaire();
|
|
3773
3709
|
let result;
|
|
3774
|
-
if (
|
|
3710
|
+
if (!mcpOnly) {
|
|
3711
|
+
console.log(chalk5.dim("\n Analyzing with AI...\n"));
|
|
3712
|
+
}
|
|
3713
|
+
try {
|
|
3714
|
+
result = await agentMatch(systems, prefs);
|
|
3715
|
+
} catch (err) {
|
|
3775
3716
|
if (!mcpOnly) {
|
|
3776
|
-
console.log(
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
result = await agentMatch(systems, prefs);
|
|
3780
|
-
} catch (err) {
|
|
3781
|
-
if (!mcpOnly) {
|
|
3782
|
-
console.log(
|
|
3783
|
-
chalk5.yellow(
|
|
3784
|
-
` AI matching failed (${err instanceof Error ? err.message : "unknown error"}), using static analysis...
|
|
3717
|
+
console.log(
|
|
3718
|
+
chalk5.yellow(
|
|
3719
|
+
` AI matching unavailable (${err instanceof Error ? err.message : "unknown error"}), using static analysis...
|
|
3785
3720
|
`
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
result = match(systems, prefs);
|
|
3790
|
-
}
|
|
3791
|
-
} else {
|
|
3792
|
-
if (!mcpOnly) {
|
|
3793
|
-
console.log(chalk5.dim("\n Analyzing your preferences...\n"));
|
|
3721
|
+
)
|
|
3722
|
+
);
|
|
3723
|
+
console.log(chalk5.dim(" Analyzing your preferences...\n"));
|
|
3794
3724
|
}
|
|
3795
3725
|
result = match(systems, prefs);
|
|
3796
3726
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "design-system-selector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI tool that recommends alternative design systems based on your project needs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"homepage": "https://github.com/TheLostByGass/design-system-selector#readme",
|
|
31
31
|
"scripts": {
|
|
32
32
|
"dev": "tsx --env-file=.env src/index.ts",
|
|
33
|
+
"vercel-build": "echo 'No build step required for Vercel API deployment'",
|
|
33
34
|
"lint": "eslint src tests",
|
|
34
35
|
"format:check": "prettier --check .",
|
|
35
36
|
"typecheck": "tsc --noEmit",
|
|
@@ -47,16 +48,15 @@
|
|
|
47
48
|
],
|
|
48
49
|
"license": "MIT",
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@anthropic-ai/sdk": "^0.77.0",
|
|
51
51
|
"@inquirer/prompts": "^7.0.0",
|
|
52
52
|
"chalk": "^5.3.0",
|
|
53
53
|
"ora": "^9.3.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"dotenv": "^17.3.1",
|
|
57
56
|
"@eslint/js": "^10.0.1",
|
|
58
57
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
59
58
|
"@types/node": "^22.0.0",
|
|
59
|
+
"dotenv": "^17.3.1",
|
|
60
60
|
"eslint": "^10.0.0",
|
|
61
61
|
"prettier": "^3.8.1",
|
|
62
62
|
"tsup": "^8.0.0",
|