@hasna/skills 0.1.13 → 0.1.15
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/bin/index.js +1743 -420
- package/bin/mcp.js +791 -259
- package/dist/index.d.ts +5 -2
- package/dist/index.js +533 -75
- package/dist/lib/config.d.ts +27 -0
- package/dist/lib/config.test.d.ts +1 -0
- package/dist/lib/installer.d.ts +36 -2
- package/dist/lib/registry.d.ts +16 -0
- package/dist/lib/scheduler.d.ts +47 -0
- package/dist/types/api.d.ts +74 -0
- package/package.json +5 -2
- package/skills/_common/index.ts +4 -0
- package/skills/_common/vision.ts +374 -0
- package/skills/skill-academic-journal-matcher/bin/cli.ts +34 -0
- package/skills/skill-action-item-router/bin/cli.ts +34 -0
- package/skills/skill-ad-creative-generator/bin/cli.ts +34 -0
- package/skills/skill-advanced-math/bin/cli.ts +34 -0
- package/skills/skill-analyze-data/bin/cli.ts +19 -0
- package/skills/skill-anomaly-investigator/bin/cli.ts +34 -0
- package/skills/skill-api-test-suite/bin/cli.ts +34 -0
- package/skills/skill-apidocs/bin/cli.ts +87 -0
- package/skills/skill-audio-cleanup-lab/bin/cli.ts +6 -0
- package/skills/skill-audiobook-chapter-proofer/bin/cli.ts +34 -0
- package/skills/skill-banner-ad-suite/bin/cli.ts +34 -0
- package/skills/skill-benchmark-finder/bin/cli.ts +34 -0
- package/skills/skill-bio-sequence-tool/bin/cli.ts +34 -0
- package/skills/skill-blog-topic-cluster/bin/cli.ts +34 -0
- package/skills/skill-brand-style-guide/bin/cli.ts +19 -0
- package/skills/skill-brand-voice-audit/bin/cli.ts +34 -0
- package/skills/skill-budget-variance-analyzer/bin/cli.ts +6 -0
- package/skills/skill-businessactivity/bin/cli.ts +28 -0
- package/skills/skill-calendar-events/bin/cli.ts +34 -0
- package/skills/skill-campaign-metric-brief/bin/cli.ts +34 -0
- package/skills/skill-campaign-moodboard/bin/cli.ts +34 -0
- package/skills/skill-caption-style-stylist/bin/cli.ts +34 -0
- package/skills/skill-chemistry-calculator/bin/cli.ts +34 -0
- package/skills/skill-churn-risk-notifier/bin/cli.ts +34 -0
- package/skills/skill-citation-formatter/bin/cli.ts +34 -0
- package/skills/skill-classroom-newsletter-kit/bin/cli.ts +34 -0
- package/skills/skill-color-palette-harmonizer/bin/cli.ts +34 -0
- package/skills/skill-colorextract/SKILL.md +35 -0
- package/skills/skill-colorextract/bun.lock +102 -0
- package/skills/skill-colorextract/package.json +13 -0
- package/skills/skill-colorextract/src/index.ts +405 -0
- package/skills/skill-competitor-ad-analyzer/bin/cli.ts +34 -0
- package/skills/skill-compliance-copy-check/bin/cli.ts +34 -0
- package/skills/skill-compliance-report-pack/bin/cli.ts +34 -0
- package/skills/skill-compress-video/bin/cli.ts +19 -0
- package/skills/skill-consolelog/bin/cli.ts +884 -0
- package/skills/skill-contract-plainlanguage/bin/cli.ts +34 -0
- package/skills/skill-copytone-translator/bin/cli.ts +34 -0
- package/skills/skill-create-blog-article/bin/cli.ts +34 -0
- package/skills/skill-create-ebook/bin/cli.ts +34 -0
- package/skills/skill-crm-note-enhancer/bin/cli.ts +34 -0
- package/skills/skill-customer-journey-mapper/bin/cli.ts +34 -0
- package/skills/skill-dashboard-builder/bin/cli.ts +34 -0
- package/skills/skill-dashboard-narrator/bin/cli.ts +34 -0
- package/skills/skill-data-anonymizer/bin/cli.ts +34 -0
- package/skills/skill-database-explorer/bin/cli.ts +34 -0
- package/skills/skill-dataset-health-check/bin/cli.ts +34 -0
- package/skills/skill-decision-journal/bin/cli.ts +34 -0
- package/skills/skill-delegation-brief-writer/bin/cli.ts +34 -0
- package/skills/skill-destination-briefing/bin/cli.ts +34 -0
- package/skills/skill-diff-viewer/bin/cli.ts +34 -0
- package/skills/skill-domainpurchase/SKILL.md +46 -0
- package/skills/skill-domainpurchase/bin/cli.ts +683 -0
- package/skills/skill-domainsearch/SKILL.md +41 -0
- package/skills/skill-domainsearch/bin/cli.ts +410 -0
- package/skills/skill-educational-resource-finder/bin/cli.ts +34 -0
- package/skills/skill-email-campaign/bin/cli.ts +34 -0
- package/skills/skill-exam-readiness-check/bin/cli.ts +34 -0
- package/skills/skill-experiment-power-calculator/bin/cli.ts +34 -0
- package/skills/skill-extract-audio/bin/cli.ts +19 -0
- package/skills/skill-extract-frames/bin/cli.ts +34 -0
- package/skills/skill-extract-invoice/bin/cli.ts +34 -0
- package/skills/skill-family-activity-curator/bin/cli.ts +34 -0
- package/skills/skill-faq-packager/bin/cli.ts +34 -0
- package/skills/skill-feedback-survey-designer/bin/cli.ts +34 -0
- package/skills/skill-field-trip-planner/bin/cli.ts +34 -0
- package/skills/skill-file-organizer/bin/cli.ts +34 -0
- package/skills/skill-folder-tree/bin/cli.ts +34 -0
- package/skills/skill-forecast-scenario-lab/bin/cli.ts +34 -0
- package/skills/skill-form-filler/bin/cli.ts +34 -0
- package/skills/skill-generate-api-client/bin/cli.ts +34 -0
- package/skills/skill-generate-book-cover/bin/cli.ts +34 -0
- package/skills/skill-generate-chart/bin/cli.ts +34 -0
- package/skills/skill-generate-diagram/bin/cli.ts +34 -0
- package/skills/skill-generate-dockerfile/bin/cli.ts +34 -0
- package/skills/skill-generate-documentation/bin/cli.ts +34 -0
- package/skills/skill-generate-docx/bin/cli.ts +6 -0
- package/skills/skill-generate-env/bin/cli.ts +34 -0
- package/skills/skill-generate-excel/bin/cli.ts +34 -0
- package/skills/skill-generate-favicon/bin/cli.ts +34 -0
- package/skills/skill-generate-mock-data/bin/cli.ts +34 -0
- package/skills/skill-generate-pdf/bin/cli.ts +6 -0
- package/skills/skill-generate-pr-description/bin/cli.ts +34 -0
- package/skills/skill-generate-presentation/bin/cli.ts +34 -0
- package/skills/skill-generate-qrcode/bin/cli.ts +34 -0
- package/skills/skill-generate-regex/bin/cli.ts +34 -0
- package/skills/skill-generate-resume/bin/cli.ts +34 -0
- package/skills/skill-generate-sitemap/bin/cli.ts +34 -0
- package/skills/skill-generate-social-posts/bin/cli.ts +34 -0
- package/skills/skill-generate-sql/bin/cli.ts +34 -0
- package/skills/skill-gif-maker/bin/cli.ts +34 -0
- package/skills/skill-github-manager/bin/cli.ts +34 -0
- package/skills/skill-gmail/bin/cli.ts +34 -0
- package/skills/skill-goal-quarterly-roadmap/bin/cli.ts +34 -0
- package/skills/skill-grant-application-drafter/bin/cli.ts +34 -0
- package/skills/skill-grocery-basket-optimizer/bin/cli.ts +34 -0
- package/skills/skill-guest-communication-suite/bin/cli.ts +34 -0
- package/skills/skill-habit-reflection-digest/bin/cli.ts +34 -0
- package/skills/skill-highlight-reel-generator/bin/cli.ts +34 -0
- package/skills/skill-homework-feedback-coach/bin/cli.ts +34 -0
- package/skills/skill-hook/bunfig.toml +5 -0
- package/skills/skill-household-maintenance-mgr/bin/cli.ts +34 -0
- package/skills/skill-http-server/bin/cli.ts +34 -0
- package/skills/skill-implementation/bunfig.toml +5 -0
- package/skills/skill-implementation-agent/bin/cli.ts +34 -0
- package/skills/skill-implementation-plan/bin/cli.ts +34 -0
- package/skills/skill-implementation-todo/bin/cli.ts +34 -0
- package/skills/skill-inbox-priority-planner/bin/cli.ts +34 -0
- package/skills/skill-invoice/bin/cli.ts +20 -0
- package/skills/skill-invoice-dispute-helper/bin/cli.ts +34 -0
- package/skills/skill-itinerary-architect/bin/cli.ts +34 -0
- package/skills/skill-jingle-composer/bin/cli.ts +34 -0
- package/skills/skill-kpi-digest-generator/bin/cli.ts +34 -0
- package/skills/skill-lab-notebook-formatter/bin/cli.ts +34 -0
- package/skills/skill-landing-page-copy/bin/cli.ts +34 -0
- package/skills/skill-latex-table-generator/bin/cli.ts +34 -0
- package/skills/skill-learning-style-profiler/bin/cli.ts +34 -0
- package/skills/skill-lesson-plan-customizer/bin/cli.ts +34 -0
- package/skills/skill-livestream-runofshow/bin/cli.ts +34 -0
- package/skills/skill-longform-structurer/bin/cli.ts +34 -0
- package/skills/skill-lorem-generator/bin/cli.ts +34 -0
- package/skills/skill-managehook/bin/cli.ts +241 -0
- package/skills/skill-managemcp/bin/cli.ts +241 -0
- package/skills/skill-manageskill/bin/cli.ts +241 -0
- package/skills/skill-markdown-validator/bin/cli.ts +34 -0
- package/skills/skill-mcp-builder/bin/cli.ts +34 -0
- package/skills/skill-meal-plan-designer/bin/cli.ts +34 -0
- package/skills/skill-meeting-insight-summarizer/bin/cli.ts +34 -0
- package/skills/skill-merge-pdfs/bin/cli.ts +34 -0
- package/skills/skill-microcopy-generator/bin/cli.ts +34 -0
- package/skills/skill-mindfulness-prompt-cache/bin/cli.ts +34 -0
- package/skills/skill-notion-manager/bin/cli.ts +34 -0
- package/skills/skill-onboarding-sequence-builder/bin/cli.ts +34 -0
- package/skills/skill-onsite-ops-checklist/bin/cli.ts +34 -0
- package/skills/skill-outreach-cadence-designer/bin/cli.ts +34 -0
- package/skills/skill-packaging-concept-studio/bin/cli.ts +34 -0
- package/skills/skill-packing-plan-pro/bin/cli.ts +34 -0
- package/skills/skill-parent-teacher-brief/bin/cli.ts +34 -0
- package/skills/skill-partner-kit-assembler/bin/cli.ts +34 -0
- package/skills/skill-payroll-change-prepper/bin/cli.ts +34 -0
- package/skills/skill-persona-based-adwriter/bin/cli.ts +34 -0
- package/skills/skill-persona-generator/bin/cli.ts +34 -0
- package/skills/skill-personal-daily-ops/bin/cli.ts +34 -0
- package/skills/skill-pet-care-scheduler/bin/cli.ts +34 -0
- package/skills/skill-podcast-show-notes/bin/cli.ts +34 -0
- package/skills/skill-presentation-theme-maker/bin/cli.ts +34 -0
- package/skills/skill-press-release-drafter/bin/cli.ts +34 -0
- package/skills/skill-print-collateral-designer/bin/cli.ts +34 -0
- package/skills/skill-procurement-scorecard/bin/cli.ts +34 -0
- package/skills/skill-product-demo-script/bin/cli.ts +34 -0
- package/skills/skill-product-mockup/bin/cli.ts +34 -0
- package/skills/skill-project-retro-companion/bin/cli.ts +34 -0
- package/skills/skill-proposal-redline-advisor/bin/cli.ts +34 -0
- package/skills/skill-regex-tester/bin/cli.ts +34 -0
- package/skills/skill-remove-background/bin/cli.ts +34 -0
- package/skills/skill-risk-disclosure-kit/bin/cli.ts +34 -0
- package/skills/skill-roi-comparison-tool/bin/cli.ts +34 -0
- package/skills/skill-sales-call-recapper/bin/cli.ts +34 -0
- package/skills/skill-salescopy/bin/cli.ts +20 -0
- package/skills/skill-scaffold-project/bin/cli.ts +34 -0
- package/skills/skill-scholarship-tracker/bin/cli.ts +34 -0
- package/skills/skill-scientific-figure-check/bin/cli.ts +34 -0
- package/skills/skill-seating-chart-maker/bin/cli.ts +34 -0
- package/skills/skill-security-audit/bin/cli.ts +34 -0
- package/skills/skill-seo-brief-builder/bin/cli.ts +34 -0
- package/skills/skill-siteanalyze/SKILL.md +25 -0
- package/skills/skill-siteanalyze/package.json +13 -0
- package/skills/skill-siteanalyze/src/index.ts +592 -0
- package/skills/skill-slack-assistant/bin/cli.ts +34 -0
- package/skills/skill-sleep-routine-analyzer/bin/cli.ts +34 -0
- package/skills/skill-social-media-kit/bin/cli.ts +34 -0
- package/skills/skill-split-pdf/bin/cli.ts +34 -0
- package/skills/skill-sponsorship-proposal-lab/bin/cli.ts +34 -0
- package/skills/skill-spreadsheet-cleanroom/bin/cli.ts +34 -0
- package/skills/skill-statistical-test-selector/bin/cli.ts +34 -0
- package/skills/skill-stress-relief-playbook/bin/cli.ts +34 -0
- package/skills/skill-study-guide-builder/bin/cli.ts +34 -0
- package/skills/skill-subscription-spend-watcher/bin/cli.ts +34 -0
- package/skills/skill-subtitle/bin/cli.ts +20 -0
- package/skills/skill-survey-insight-extractor/bin/cli.ts +34 -0
- package/skills/skill-terraform-generator/bin/cli.ts +34 -0
- package/skills/skill-testimonial-graphics/bin/cli.ts +34 -0
- package/skills/skill-timesheet/bin/cli.ts +47 -0
- package/skills/skill-travel-budget-balancer/bin/cli.ts +34 -0
- package/skills/skill-validate-config/bin/cli.ts +34 -0
- package/skills/skill-video-cut-suggester/bin/cli.ts +34 -0
- package/skills/skill-video-downloader/bin/cli.ts +34 -0
- package/skills/skill-video-thumbnail/bin/cli.ts +34 -0
- package/skills/skill-voiceover-casting-assistant/bin/cli.ts +34 -0
- package/skills/skill-watermark/bin/cli.ts +34 -0
- package/skills/skill-webcrawling/bin/cli.ts +21 -0
- package/skills/skill-webinar-script-coach/bin/cli.ts +34 -0
- package/skills/skill-wellness-progress-reporter/bin/cli.ts +34 -0
- package/skills/skill-workout-cycle-planner/bin/cli.ts +34 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config file support for Open Skills
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration from:
|
|
5
|
+
* 1. Project-local: ./skills.config.json (highest priority)
|
|
6
|
+
* 2. Global: ~/.skillsrc (JSON format, lowest priority)
|
|
7
|
+
*
|
|
8
|
+
* Values from the project config override global config.
|
|
9
|
+
*/
|
|
10
|
+
export interface SkillsConfig {
|
|
11
|
+
defaultAgent?: "claude" | "codex" | "gemini" | "all";
|
|
12
|
+
defaultScope?: "global" | "project";
|
|
13
|
+
format?: "compact" | "json" | "csv";
|
|
14
|
+
}
|
|
15
|
+
export type ConfigScope = "global" | "project";
|
|
16
|
+
/**
|
|
17
|
+
* Get the config file path for a given scope
|
|
18
|
+
*/
|
|
19
|
+
export declare function getConfigPath(scope: ConfigScope): string;
|
|
20
|
+
/**
|
|
21
|
+
* Load merged config: project-local overrides global
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadConfig(): SkillsConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Save a single config key-value pair to the specified scope
|
|
26
|
+
*/
|
|
27
|
+
export declare function saveConfig(key: string, value: string, scope?: ConfigScope): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/lib/installer.d.ts
CHANGED
|
@@ -27,6 +27,30 @@ export declare function installSkill(name: string, options?: InstallOptions): In
|
|
|
27
27
|
* Install multiple skills
|
|
28
28
|
*/
|
|
29
29
|
export declare function installSkills(names: string[], options?: InstallOptions): InstallResult[];
|
|
30
|
+
interface SkillMeta {
|
|
31
|
+
installedAt: string;
|
|
32
|
+
version: string;
|
|
33
|
+
}
|
|
34
|
+
interface MetaFile {
|
|
35
|
+
skills: Record<string, SkillMeta>;
|
|
36
|
+
disabled?: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get installation metadata for installed skills
|
|
40
|
+
*/
|
|
41
|
+
export declare function getInstallMeta(targetDir?: string): MetaFile;
|
|
42
|
+
/**
|
|
43
|
+
* Disable a skill (exclude from .skills/index.ts without removing files)
|
|
44
|
+
*/
|
|
45
|
+
export declare function disableSkill(name: string, targetDir?: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Enable a previously disabled skill (re-add to .skills/index.ts)
|
|
48
|
+
*/
|
|
49
|
+
export declare function enableSkill(name: string, targetDir?: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Get list of disabled skills
|
|
52
|
+
*/
|
|
53
|
+
export declare function getDisabledSkills(targetDir?: string): string[];
|
|
30
54
|
/**
|
|
31
55
|
* Get list of installed skills in a directory
|
|
32
56
|
*/
|
|
@@ -35,9 +59,11 @@ export declare function getInstalledSkills(targetDir?: string): string[];
|
|
|
35
59
|
* Remove an installed skill
|
|
36
60
|
*/
|
|
37
61
|
export declare function removeSkill(name: string, targetDir?: string): boolean;
|
|
38
|
-
export type AgentTarget = "claude" | "codex" | "gemini";
|
|
62
|
+
export type AgentTarget = "claude" | "codex" | "gemini" | "pi" | "opencode";
|
|
39
63
|
export type AgentScope = "global" | "project";
|
|
40
64
|
export declare const AGENT_TARGETS: AgentTarget[];
|
|
65
|
+
/** Human-readable labels for each agent */
|
|
66
|
+
export declare const AGENT_LABELS: Record<AgentTarget, string>;
|
|
41
67
|
/**
|
|
42
68
|
* Resolve an agent argument ("all" or a specific agent name) to a list of AgentTarget values.
|
|
43
69
|
* Throws if the agent name is not recognized.
|
|
@@ -49,7 +75,14 @@ export interface AgentInstallOptions {
|
|
|
49
75
|
projectDir?: string;
|
|
50
76
|
}
|
|
51
77
|
/**
|
|
52
|
-
* Get the skills directory for a given agent and scope
|
|
78
|
+
* Get the skills directory for a given agent and scope.
|
|
79
|
+
*
|
|
80
|
+
* Agent config dir conventions:
|
|
81
|
+
* claude — ~/.claude/skills/ | .claude/skills/
|
|
82
|
+
* codex — ~/.codex/skills/ | .codex/skills/
|
|
83
|
+
* gemini — ~/.gemini/skills/ | .gemini/skills/
|
|
84
|
+
* pi — ~/.pi/agent/skills/ | .pi/skills/
|
|
85
|
+
* opencode — ~/.opencode/skills/ | .opencode/skills/
|
|
53
86
|
*/
|
|
54
87
|
export declare function getAgentSkillsDir(agent: AgentTarget, scope?: AgentScope, projectDir?: string): string;
|
|
55
88
|
/**
|
|
@@ -65,3 +98,4 @@ export declare function installSkillForAgent(name: string, options: AgentInstall
|
|
|
65
98
|
* Remove a skill from an agent's skill directory
|
|
66
99
|
*/
|
|
67
100
|
export declare function removeSkillForAgent(name: string, options: AgentInstallOptions): boolean;
|
|
101
|
+
export {};
|
package/dist/lib/registry.d.ts
CHANGED
|
@@ -8,10 +8,22 @@ export interface SkillMeta {
|
|
|
8
8
|
category: string;
|
|
9
9
|
tags: string[];
|
|
10
10
|
dependencies?: string[];
|
|
11
|
+
source?: "official" | "custom";
|
|
11
12
|
}
|
|
12
13
|
export declare const CATEGORIES: readonly ["Development Tools", "Business & Marketing", "Productivity & Organization", "Project Management", "Content Generation", "Finance & Compliance", "Data & Analysis", "Media Processing", "Design & Branding", "Web & Browser", "Research & Writing", "Science & Academic", "Education & Learning", "Communication", "Health & Wellness", "Travel & Lifestyle", "Event Management"];
|
|
13
14
|
export type Category = (typeof CATEGORIES)[number];
|
|
14
15
|
export declare const SKILLS: SkillMeta[];
|
|
16
|
+
/**
|
|
17
|
+
* Load the full registry: official skills merged with custom skills from:
|
|
18
|
+
* - ~/.skills/ (global custom)
|
|
19
|
+
* - ./.custom-skills/ (project-level custom, relative to cwd)
|
|
20
|
+
*
|
|
21
|
+
* Custom skills with the same name as official skills take precedence.
|
|
22
|
+
* Results are cached for 5 seconds.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadRegistry(cwd?: string): SkillMeta[];
|
|
25
|
+
/** Invalidate the registry cache (e.g. after installing a custom skill). */
|
|
26
|
+
export declare function clearRegistryCache(): void;
|
|
15
27
|
export declare function getSkillsByCategory(category: Category): SkillMeta[];
|
|
16
28
|
export declare function searchSkills(query: string): SkillMeta[];
|
|
17
29
|
export declare function getSkill(name: string): SkillMeta | undefined;
|
|
@@ -23,3 +35,7 @@ export declare function getSkillsByTag(tag: string): SkillMeta[];
|
|
|
23
35
|
* Return all unique tags across every skill, sorted alphabetically.
|
|
24
36
|
*/
|
|
25
37
|
export declare function getAllTags(): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Find skills with names similar to the given query (for "did you mean?" suggestions)
|
|
40
|
+
*/
|
|
41
|
+
export declare function findSimilarSkills(query: string, maxResults?: number): string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill scheduler — cron-based scheduling for skills
|
|
3
|
+
*
|
|
4
|
+
* Schedules are stored in .skills/schedules.json in the project directory.
|
|
5
|
+
* Each schedule entry defines a skill to run on a cron expression.
|
|
6
|
+
*
|
|
7
|
+
* Cron format: standard 5-field (minute hour dom month dow)
|
|
8
|
+
* e.g. "0 9 * * *" = every day at 9am
|
|
9
|
+
*/
|
|
10
|
+
export interface SkillSchedule {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
skill: string;
|
|
14
|
+
cron: string;
|
|
15
|
+
args?: string[];
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
lastRun?: string;
|
|
19
|
+
lastRunStatus?: "success" | "error";
|
|
20
|
+
nextRun?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Validate a 5-field cron expression (basic syntax check). */
|
|
23
|
+
export declare function validateCron(expr: string): {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Compute the next run time for a cron expression relative to a given date. */
|
|
28
|
+
export declare function getNextRun(cron: string, from?: Date): Date | null;
|
|
29
|
+
/** Add a new schedule. Returns the created schedule. */
|
|
30
|
+
export declare function addSchedule(skill: string, cron: string, options?: {
|
|
31
|
+
name?: string;
|
|
32
|
+
args?: string[];
|
|
33
|
+
targetDir?: string;
|
|
34
|
+
}): {
|
|
35
|
+
schedule: SkillSchedule | null;
|
|
36
|
+
error?: string;
|
|
37
|
+
};
|
|
38
|
+
/** List all schedules. */
|
|
39
|
+
export declare function listSchedules(targetDir?: string): SkillSchedule[];
|
|
40
|
+
/** Remove a schedule by id or name. Returns true if removed. */
|
|
41
|
+
export declare function removeSchedule(idOrName: string, targetDir?: string): boolean;
|
|
42
|
+
/** Enable or disable a schedule by id or name. */
|
|
43
|
+
export declare function setScheduleEnabled(idOrName: string, enabled: boolean, targetDir?: string): boolean;
|
|
44
|
+
/** Get all schedules that are due now (nextRun <= now and enabled). */
|
|
45
|
+
export declare function getDueSchedules(targetDir?: string): SkillSchedule[];
|
|
46
|
+
/** Mark a schedule as having just run. Updates lastRun and nextRun. */
|
|
47
|
+
export declare function recordScheduleRun(id: string, status: "success" | "error", targetDir?: string): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared API response types used by both the HTTP server and the dashboard.
|
|
3
|
+
* Import from "@hasna/skills" to get type-safe API responses.
|
|
4
|
+
*/
|
|
5
|
+
export interface SkillResponse {
|
|
6
|
+
name: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
description: string;
|
|
9
|
+
category: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
installed: boolean;
|
|
12
|
+
envVars: string[];
|
|
13
|
+
envVarsSet: string[];
|
|
14
|
+
systemDeps: string[];
|
|
15
|
+
cliCommand: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface SkillDetailResponse extends SkillResponse {
|
|
18
|
+
docs: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface CategoryResponse {
|
|
21
|
+
name: string;
|
|
22
|
+
count: number;
|
|
23
|
+
}
|
|
24
|
+
export interface TagResponse {
|
|
25
|
+
name: string;
|
|
26
|
+
count: number;
|
|
27
|
+
}
|
|
28
|
+
export interface InstallResponse {
|
|
29
|
+
skill: string;
|
|
30
|
+
success: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
results?: Array<{
|
|
33
|
+
skill: string;
|
|
34
|
+
success: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
export interface RemoveResponse {
|
|
39
|
+
skill: string;
|
|
40
|
+
success: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface VersionResponse {
|
|
43
|
+
version: string;
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
export interface ExportResponse {
|
|
47
|
+
version: number;
|
|
48
|
+
skills: string[];
|
|
49
|
+
timestamp: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ImportResponse {
|
|
52
|
+
imported: number;
|
|
53
|
+
total: number;
|
|
54
|
+
results: Array<{
|
|
55
|
+
skill: string;
|
|
56
|
+
success: boolean;
|
|
57
|
+
error?: string;
|
|
58
|
+
}>;
|
|
59
|
+
}
|
|
60
|
+
export interface SearchResponse extends SkillResponse {
|
|
61
|
+
}
|
|
62
|
+
export interface CategoryInstallResponse {
|
|
63
|
+
category: string;
|
|
64
|
+
count: number;
|
|
65
|
+
success: boolean;
|
|
66
|
+
results: Array<{
|
|
67
|
+
skill: string;
|
|
68
|
+
success: boolean;
|
|
69
|
+
error?: string;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
export interface ErrorResponse {
|
|
73
|
+
error: string;
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Skills library for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
"main": "./dist/index.js",
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
25
|
"scripts": {
|
|
26
|
-
"
|
|
26
|
+
"clean": "rm -rf bin/ dist/",
|
|
27
|
+
"build": "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun --external ink --external react --external chalk && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
27
28
|
"test": "bun test",
|
|
28
29
|
"dev": "bun run ./src/cli/index.tsx",
|
|
30
|
+
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
31
|
+
"dev:mcp": "bun --watch run ./src/mcp/index.ts",
|
|
29
32
|
"typecheck": "tsc --noEmit",
|
|
30
33
|
"prepublishOnly": "bun run build",
|
|
31
34
|
"dashboard:dev": "cd dashboard && bun run dev",
|
package/skills/_common/index.ts
CHANGED
|
@@ -21,3 +21,7 @@ export {
|
|
|
21
21
|
type AssistantType,
|
|
22
22
|
} from './installer';
|
|
23
23
|
export { handleInstallCommand } from './skill-install';
|
|
24
|
+
|
|
25
|
+
// Vision client — multi-provider (anthropic/openai/xai/gemini)
|
|
26
|
+
// import { analyzeImage, detectProvider } from './vision.js'
|
|
27
|
+
export * from './vision.js';
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified vision client for AI providers.
|
|
3
|
+
* Follows @hasna/connectors pattern — auto-detects from env vars.
|
|
4
|
+
* Providers: anthropic, openai, xai, gemini
|
|
5
|
+
*
|
|
6
|
+
* Uses built-in fetch only — no external SDK dependencies.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type VisionProvider = "anthropic" | "openai" | "xai" | "gemini";
|
|
10
|
+
|
|
11
|
+
export interface VisionOptions {
|
|
12
|
+
provider?: VisionProvider;
|
|
13
|
+
model?: string;
|
|
14
|
+
maxTokens?: number;
|
|
15
|
+
systemPrompt?: string;
|
|
16
|
+
jsonMode?: boolean; // wrap prompt to request JSON response
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface VisionResult {
|
|
20
|
+
text: string;
|
|
21
|
+
provider: VisionProvider;
|
|
22
|
+
model: string;
|
|
23
|
+
inputTokens?: number;
|
|
24
|
+
outputTokens?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Default models per provider (vision-capable)
|
|
28
|
+
export const DEFAULT_MODELS: Record<VisionProvider, string> = {
|
|
29
|
+
anthropic: "claude-sonnet-4-6",
|
|
30
|
+
openai: "gpt-4o",
|
|
31
|
+
xai: "grok-2-vision-1212",
|
|
32
|
+
gemini: "gemini-2.0-flash",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// API key env vars per provider
|
|
36
|
+
export const API_KEY_VARS: Record<VisionProvider, string> = {
|
|
37
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
38
|
+
openai: "OPENAI_API_KEY",
|
|
39
|
+
xai: "XAI_API_KEY",
|
|
40
|
+
gemini: "GEMINI_API_KEY",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Base URLs
|
|
44
|
+
const BASE_URLS: Record<VisionProvider, string> = {
|
|
45
|
+
anthropic: "https://api.anthropic.com/v1/messages",
|
|
46
|
+
openai: "https://api.openai.com/v1/chat/completions",
|
|
47
|
+
xai: "https://api.x.ai/v1/chat/completions",
|
|
48
|
+
gemini: "https://generativelanguage.googleapis.com/v1beta/models",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Auto-detect provider from env vars.
|
|
53
|
+
* Priority: ANTHROPIC_API_KEY → OPENAI_API_KEY → XAI_API_KEY → GEMINI_API_KEY
|
|
54
|
+
*/
|
|
55
|
+
export function detectProvider(): VisionProvider | null {
|
|
56
|
+
const order: VisionProvider[] = ["anthropic", "openai", "xai", "gemini"];
|
|
57
|
+
for (const provider of order) {
|
|
58
|
+
if (process.env[API_KEY_VARS[provider]]) {
|
|
59
|
+
return provider;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get API key for a provider. Throws if not set.
|
|
67
|
+
*/
|
|
68
|
+
export function getApiKey(provider: VisionProvider): string {
|
|
69
|
+
const key = process.env[API_KEY_VARS[provider]];
|
|
70
|
+
if (!key) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`${API_KEY_VARS[provider]} is not set. Run: connectors setup ${provider}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return key;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Strip markdown code fences and JSON.parse.
|
|
80
|
+
* Throws with raw text on failure.
|
|
81
|
+
*/
|
|
82
|
+
export function parseJsonResponse(text: string): unknown {
|
|
83
|
+
let str = text.trim();
|
|
84
|
+
if (str.startsWith("```")) {
|
|
85
|
+
str = str
|
|
86
|
+
.replace(/^```(?:json)?\n?/, "")
|
|
87
|
+
.replace(/\n?```$/, "")
|
|
88
|
+
.trim();
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(str);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Failed to parse response as JSON.\nRaw response:\n${text}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns all providers that have an API key set.
|
|
101
|
+
*/
|
|
102
|
+
export function listAvailableProviders(): VisionProvider[] {
|
|
103
|
+
return (["anthropic", "openai", "xai", "gemini"] as VisionProvider[]).filter(
|
|
104
|
+
(p) => !!process.env[API_KEY_VARS[p]]
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns availability status for all providers.
|
|
110
|
+
*/
|
|
111
|
+
export function getProviderInfo(): Record<
|
|
112
|
+
VisionProvider,
|
|
113
|
+
{ available: boolean; model: string; keyVar: string }
|
|
114
|
+
> {
|
|
115
|
+
return {
|
|
116
|
+
anthropic: {
|
|
117
|
+
available: !!process.env[API_KEY_VARS.anthropic],
|
|
118
|
+
model: DEFAULT_MODELS.anthropic,
|
|
119
|
+
keyVar: API_KEY_VARS.anthropic,
|
|
120
|
+
},
|
|
121
|
+
openai: {
|
|
122
|
+
available: !!process.env[API_KEY_VARS.openai],
|
|
123
|
+
model: DEFAULT_MODELS.openai,
|
|
124
|
+
keyVar: API_KEY_VARS.openai,
|
|
125
|
+
},
|
|
126
|
+
xai: {
|
|
127
|
+
available: !!process.env[API_KEY_VARS.xai],
|
|
128
|
+
model: DEFAULT_MODELS.xai,
|
|
129
|
+
keyVar: API_KEY_VARS.xai,
|
|
130
|
+
},
|
|
131
|
+
gemini: {
|
|
132
|
+
available: !!process.env[API_KEY_VARS.gemini],
|
|
133
|
+
model: DEFAULT_MODELS.gemini,
|
|
134
|
+
keyVar: API_KEY_VARS.gemini,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Provider implementations
|
|
141
|
+
// ============================================================================
|
|
142
|
+
|
|
143
|
+
async function analyzeWithAnthropic(
|
|
144
|
+
imageBase64: string,
|
|
145
|
+
mediaType: string,
|
|
146
|
+
prompt: string,
|
|
147
|
+
model: string,
|
|
148
|
+
options: VisionOptions
|
|
149
|
+
): Promise<VisionResult> {
|
|
150
|
+
const apiKey = getApiKey("anthropic");
|
|
151
|
+
const body: Record<string, unknown> = {
|
|
152
|
+
model,
|
|
153
|
+
max_tokens: options.maxTokens ?? 1024,
|
|
154
|
+
messages: [
|
|
155
|
+
{
|
|
156
|
+
role: "user",
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "image",
|
|
160
|
+
source: { type: "base64", media_type: mediaType, data: imageBase64 },
|
|
161
|
+
},
|
|
162
|
+
{ type: "text", text: prompt },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
if (options.systemPrompt) {
|
|
168
|
+
body.system = options.systemPrompt;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const response = await fetch(BASE_URLS.anthropic, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: {
|
|
174
|
+
"x-api-key": apiKey,
|
|
175
|
+
"anthropic-version": "2023-06-01",
|
|
176
|
+
"content-type": "application/json",
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify(body),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
const err = await response.text();
|
|
183
|
+
throw new Error(`Anthropic API error ${response.status}: ${err}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const data = (await response.json()) as {
|
|
187
|
+
content: Array<{ type: string; text: string }>;
|
|
188
|
+
usage: { input_tokens: number; output_tokens: number };
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
text: data.content[0].text,
|
|
193
|
+
provider: "anthropic",
|
|
194
|
+
model,
|
|
195
|
+
inputTokens: data.usage?.input_tokens,
|
|
196
|
+
outputTokens: data.usage?.output_tokens,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function analyzeWithOpenAICompat(
|
|
201
|
+
provider: "openai" | "xai",
|
|
202
|
+
imageBase64: string,
|
|
203
|
+
mediaType: string,
|
|
204
|
+
prompt: string,
|
|
205
|
+
model: string,
|
|
206
|
+
options: VisionOptions
|
|
207
|
+
): Promise<VisionResult> {
|
|
208
|
+
const apiKey = getApiKey(provider);
|
|
209
|
+
const messages: unknown[] = [];
|
|
210
|
+
|
|
211
|
+
if (options.systemPrompt) {
|
|
212
|
+
messages.push({ role: "system", content: options.systemPrompt });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
messages.push({
|
|
216
|
+
role: "user",
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "image_url",
|
|
220
|
+
image_url: {
|
|
221
|
+
url: `data:${mediaType};base64,${imageBase64}`,
|
|
222
|
+
detail: "high",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{ type: "text", text: prompt },
|
|
226
|
+
],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const body = {
|
|
230
|
+
model,
|
|
231
|
+
max_tokens: options.maxTokens ?? 1024,
|
|
232
|
+
messages,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const response = await fetch(BASE_URLS[provider], {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: {
|
|
238
|
+
Authorization: `Bearer ${apiKey}`,
|
|
239
|
+
"content-type": "application/json",
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify(body),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
const err = await response.text();
|
|
246
|
+
throw new Error(
|
|
247
|
+
`${provider.toUpperCase()} API error ${response.status}: ${err}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const data = (await response.json()) as {
|
|
252
|
+
choices: Array<{ message: { content: string } }>;
|
|
253
|
+
usage: { prompt_tokens: number; completion_tokens: number };
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
text: data.choices[0].message.content,
|
|
258
|
+
provider,
|
|
259
|
+
model,
|
|
260
|
+
inputTokens: data.usage?.prompt_tokens,
|
|
261
|
+
outputTokens: data.usage?.completion_tokens,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function analyzeWithGemini(
|
|
266
|
+
imageBase64: string,
|
|
267
|
+
mediaType: string,
|
|
268
|
+
prompt: string,
|
|
269
|
+
model: string,
|
|
270
|
+
options: VisionOptions
|
|
271
|
+
): Promise<VisionResult> {
|
|
272
|
+
const apiKey = getApiKey("gemini");
|
|
273
|
+
const url = `${BASE_URLS.gemini}/${model}:generateContent?key=${apiKey}`;
|
|
274
|
+
|
|
275
|
+
const body: Record<string, unknown> = {
|
|
276
|
+
contents: [
|
|
277
|
+
{
|
|
278
|
+
parts: [
|
|
279
|
+
{ inlineData: { mimeType: mediaType, data: imageBase64 } },
|
|
280
|
+
{ text: prompt },
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
generationConfig: {
|
|
285
|
+
maxOutputTokens: options.maxTokens ?? 1024,
|
|
286
|
+
...(options.jsonMode ? { responseMimeType: "application/json" } : {}),
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
if (options.systemPrompt) {
|
|
291
|
+
body.systemInstruction = { parts: [{ text: options.systemPrompt }] };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const response = await fetch(url, {
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: { "content-type": "application/json" },
|
|
297
|
+
body: JSON.stringify(body),
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
const err = await response.text();
|
|
302
|
+
throw new Error(`Gemini API error ${response.status}: ${err}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const data = (await response.json()) as {
|
|
306
|
+
candidates: Array<{
|
|
307
|
+
content: { parts: Array<{ text: string }> };
|
|
308
|
+
}>;
|
|
309
|
+
usageMetadata?: { promptTokenCount: number; candidatesTokenCount: number };
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
text: data.candidates[0].content.parts[0].text,
|
|
314
|
+
provider: "gemini",
|
|
315
|
+
model,
|
|
316
|
+
inputTokens: data.usageMetadata?.promptTokenCount,
|
|
317
|
+
outputTokens: data.usageMetadata?.candidatesTokenCount,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// Main entry point
|
|
323
|
+
// ============================================================================
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Analyze an image with a vision-capable model.
|
|
327
|
+
* Auto-detects provider from env vars unless options.provider is specified.
|
|
328
|
+
*/
|
|
329
|
+
export async function analyzeImage(
|
|
330
|
+
imageBase64: string,
|
|
331
|
+
mediaType: string,
|
|
332
|
+
prompt: string,
|
|
333
|
+
options: VisionOptions = {}
|
|
334
|
+
): Promise<VisionResult> {
|
|
335
|
+
const provider = options.provider ?? detectProvider();
|
|
336
|
+
if (!provider) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
"No AI provider API key found. Set one of: ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, GEMINI_API_KEY"
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const model = options.model ?? DEFAULT_MODELS[provider];
|
|
343
|
+
const jsonPrompt =
|
|
344
|
+
options.jsonMode
|
|
345
|
+
? `${prompt}\n\nIMPORTANT: Respond ONLY with valid JSON. No markdown, no code fences, no commentary.`
|
|
346
|
+
: prompt;
|
|
347
|
+
|
|
348
|
+
switch (provider) {
|
|
349
|
+
case "anthropic":
|
|
350
|
+
return analyzeWithAnthropic(
|
|
351
|
+
imageBase64,
|
|
352
|
+
mediaType,
|
|
353
|
+
jsonPrompt,
|
|
354
|
+
model,
|
|
355
|
+
options
|
|
356
|
+
);
|
|
357
|
+
case "openai":
|
|
358
|
+
case "xai":
|
|
359
|
+
return analyzeWithOpenAICompat(
|
|
360
|
+
provider,
|
|
361
|
+
imageBase64,
|
|
362
|
+
mediaType,
|
|
363
|
+
jsonPrompt,
|
|
364
|
+
model,
|
|
365
|
+
options
|
|
366
|
+
);
|
|
367
|
+
case "gemini":
|
|
368
|
+
return analyzeWithGemini(imageBase64, mediaType, jsonPrompt, model, options);
|
|
369
|
+
default: {
|
|
370
|
+
const _exhaustive: never = provider;
|
|
371
|
+
throw new Error(`Unknown provider: ${_exhaustive}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
|
|
7
|
+
function getDataDir(): string {
|
|
8
|
+
return process.env.DATA_DIR || join(homedir(), ".skill", "skill-academic-journal-matcher");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name("skill-academic-journal-matcher")
|
|
15
|
+
.description("Academic Journal Matcher skill")
|
|
16
|
+
.version("0.1.0");
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command("run")
|
|
20
|
+
.description("Run the skill")
|
|
21
|
+
.option("-o, --output <path>", "Output directory", join(getDataDir(), "output"))
|
|
22
|
+
.allowUnknownOption(true)
|
|
23
|
+
.action(async (options, command) => {
|
|
24
|
+
try {
|
|
25
|
+
const args = command.args;
|
|
26
|
+
process.env.DATA_DIR = process.env.DATA_DIR || getDataDir();
|
|
27
|
+
await import("../src/index.js");
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
program.parse();
|