@hasna/skills 0.1.18 → 0.1.20
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 +108 -372
- package/bin/index.js +10455 -9282
- package/bin/mcp.js +4793 -3402
- package/dist/cli/commands/completion.d.ts +5 -0
- package/dist/cli/commands/create-sync-config.d.ts +5 -0
- package/dist/cli/commands/diagnostic.d.ts +5 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/install.d.ts +5 -0
- package/dist/cli/commands/introspect.d.ts +5 -0
- package/dist/cli/commands/list.d.ts +5 -0
- package/dist/cli/commands/runtime.d.ts +5 -0
- package/dist/cli/commands/schedule.d.ts +5 -0
- package/dist/index.js +197 -97
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/registry.d.ts +1 -11
- package/dist/lib/scheduler.d.ts +1 -1
- package/dist/lib/scheduler.test.d.ts +4 -0
- package/dist/lib/search.d.ts +17 -0
- package/package.json +3 -3
- package/skills/skill-commitpush/SKILL.md +57 -0
- package/skills/skill-commitpush/package.json +34 -0
- package/skills/skill-commitpush/src/index.ts +34 -0
- package/skills/skill-commitpush/tsconfig.json +17 -0
- package/skills/skill-commitpushpr/SKILL.md +55 -0
- package/skills/skill-commitpushpr/package.json +34 -0
- package/skills/skill-commitpushpr/src/index.ts +34 -0
- package/skills/skill-commitpushpr/tsconfig.json +17 -0
- package/skills/skill-monitor/SKILL.md +69 -0
- package/skills/skill-monitor/package.json +34 -0
- package/skills/skill-monitor/src/index.ts +34 -0
- package/skills/skill-monitor/tsconfig.json +17 -0
- package/skills/skill-read-csv/SKILL.md +62 -0
- package/skills/skill-read-csv/package.json +38 -0
- package/skills/skill-read-csv/src/index.ts +331 -0
- package/skills/skill-read-csv/tsconfig.json +17 -0
- package/skills/skill-read-excel/SKILL.md +64 -0
- package/skills/skill-read-excel/package.json +37 -0
- package/skills/skill-read-excel/src/index.ts +253 -0
- package/skills/skill-read-excel/tsconfig.json +17 -0
- package/skills/skill-read-image/SKILL.md +47 -0
- package/skills/skill-read-image/package.json +34 -0
- package/skills/skill-read-image/src/index.ts +264 -0
- package/skills/skill-read-image/tsconfig.json +17 -0
- package/skills/skill-read-pdf/SKILL.md +52 -0
- package/skills/skill-read-pdf/package.json +37 -0
- package/skills/skill-read-pdf/src/index.ts +376 -0
- package/skills/skill-read-pdf/tsconfig.json +17 -0
- package/skills/skill-tmux-session/SKILL.md +109 -0
- package/skills/skill-tmux-session/package.json +34 -0
- package/skills/skill-tmux-session/src/index.ts +34 -0
- package/skills/skill-tmux-session/tsconfig.json +17 -0
- package/skills/skill-academic-journal-matcher/bin/cli.ts +0 -34
- package/skills/skill-action-item-router/bin/cli.ts +0 -34
- package/skills/skill-ad-creative-generator/bin/cli.ts +0 -34
- package/skills/skill-advanced-math/bin/cli.ts +0 -34
- package/skills/skill-analyze-data/bin/cli.ts +0 -19
- package/skills/skill-anomaly-investigator/bin/cli.ts +0 -34
- package/skills/skill-api-test-suite/bin/cli.ts +0 -34
- package/skills/skill-apidocs/bin/cli.ts +0 -87
- package/skills/skill-audio-cleanup-lab/bin/cli.ts +0 -6
- package/skills/skill-audiobook-chapter-proofer/bin/cli.ts +0 -34
- package/skills/skill-banner-ad-suite/bin/cli.ts +0 -34
- package/skills/skill-benchmark-finder/bin/cli.ts +0 -34
- package/skills/skill-bio-sequence-tool/bin/cli.ts +0 -34
- package/skills/skill-blog-topic-cluster/bin/cli.ts +0 -34
- package/skills/skill-brand-style-guide/bin/cli.ts +0 -19
- package/skills/skill-brand-voice-audit/bin/cli.ts +0 -34
- package/skills/skill-budget-variance-analyzer/bin/cli.ts +0 -6
- package/skills/skill-businessactivity/bin/cli.ts +0 -28
- package/skills/skill-calendar-events/bin/cli.ts +0 -34
- package/skills/skill-campaign-metric-brief/bin/cli.ts +0 -34
- package/skills/skill-campaign-moodboard/bin/cli.ts +0 -34
- package/skills/skill-caption-style-stylist/bin/cli.ts +0 -34
- package/skills/skill-chemistry-calculator/bin/cli.ts +0 -34
- package/skills/skill-churn-risk-notifier/bin/cli.ts +0 -34
- package/skills/skill-citation-formatter/bin/cli.ts +0 -34
- package/skills/skill-classroom-newsletter-kit/bin/cli.ts +0 -34
- package/skills/skill-color-palette-harmonizer/bin/cli.ts +0 -34
- package/skills/skill-competitor-ad-analyzer/bin/cli.ts +0 -34
- package/skills/skill-compliance-copy-check/bin/cli.ts +0 -34
- package/skills/skill-compliance-report-pack/bin/cli.ts +0 -34
- package/skills/skill-compress-video/bin/cli.ts +0 -19
- package/skills/skill-consolelog/bin/cli.ts +0 -884
- package/skills/skill-contract-plainlanguage/bin/cli.ts +0 -34
- package/skills/skill-copytone-translator/bin/cli.ts +0 -34
- package/skills/skill-create-blog-article/bin/cli.ts +0 -34
- package/skills/skill-create-ebook/bin/cli.ts +0 -34
- package/skills/skill-crm-note-enhancer/bin/cli.ts +0 -34
- package/skills/skill-customer-journey-mapper/bin/cli.ts +0 -34
- package/skills/skill-dashboard-builder/bin/cli.ts +0 -34
- package/skills/skill-dashboard-narrator/bin/cli.ts +0 -34
- package/skills/skill-data-anonymizer/bin/cli.ts +0 -34
- package/skills/skill-database-explorer/bin/cli.ts +0 -34
- package/skills/skill-dataset-health-check/bin/cli.ts +0 -34
- package/skills/skill-decision-journal/bin/cli.ts +0 -34
- package/skills/skill-delegation-brief-writer/bin/cli.ts +0 -34
- package/skills/skill-destination-briefing/bin/cli.ts +0 -34
- package/skills/skill-diff-viewer/bin/cli.ts +0 -34
- package/skills/skill-domainpurchase/bin/cli.ts +0 -683
- package/skills/skill-domainsearch/bin/cli.ts +0 -410
- package/skills/skill-educational-resource-finder/bin/cli.ts +0 -34
- package/skills/skill-email-campaign/bin/cli.ts +0 -34
- package/skills/skill-exam-readiness-check/bin/cli.ts +0 -34
- package/skills/skill-experiment-power-calculator/bin/cli.ts +0 -34
- package/skills/skill-extract-audio/bin/cli.ts +0 -19
- package/skills/skill-extract-frames/bin/cli.ts +0 -34
- package/skills/skill-extract-invoice/bin/cli.ts +0 -34
- package/skills/skill-family-activity-curator/bin/cli.ts +0 -34
- package/skills/skill-faq-packager/bin/cli.ts +0 -34
- package/skills/skill-feedback-survey-designer/bin/cli.ts +0 -34
- package/skills/skill-field-trip-planner/bin/cli.ts +0 -34
- package/skills/skill-file-organizer/bin/cli.ts +0 -34
- package/skills/skill-folder-tree/bin/cli.ts +0 -34
- package/skills/skill-forecast-scenario-lab/bin/cli.ts +0 -34
- package/skills/skill-form-filler/bin/cli.ts +0 -34
- package/skills/skill-generate-api-client/bin/cli.ts +0 -34
- package/skills/skill-generate-book-cover/bin/cli.ts +0 -34
- package/skills/skill-generate-chart/bin/cli.ts +0 -34
- package/skills/skill-generate-diagram/bin/cli.ts +0 -34
- package/skills/skill-generate-dockerfile/bin/cli.ts +0 -34
- package/skills/skill-generate-documentation/bin/cli.ts +0 -34
- package/skills/skill-generate-docx/bin/cli.ts +0 -6
- package/skills/skill-generate-env/bin/cli.ts +0 -34
- package/skills/skill-generate-excel/bin/cli.ts +0 -34
- package/skills/skill-generate-favicon/bin/cli.ts +0 -34
- package/skills/skill-generate-mock-data/bin/cli.ts +0 -34
- package/skills/skill-generate-pdf/bin/cli.ts +0 -6
- package/skills/skill-generate-pr-description/bin/cli.ts +0 -34
- package/skills/skill-generate-presentation/bin/cli.ts +0 -34
- package/skills/skill-generate-qrcode/bin/cli.ts +0 -34
- package/skills/skill-generate-regex/bin/cli.ts +0 -34
- package/skills/skill-generate-resume/bin/cli.ts +0 -34
- package/skills/skill-generate-sitemap/bin/cli.ts +0 -34
- package/skills/skill-generate-social-posts/bin/cli.ts +0 -34
- package/skills/skill-generate-sql/bin/cli.ts +0 -34
- package/skills/skill-gif-maker/bin/cli.ts +0 -34
- package/skills/skill-github-manager/bin/cli.ts +0 -34
- package/skills/skill-gmail/bin/cli.ts +0 -34
- package/skills/skill-goal-quarterly-roadmap/bin/cli.ts +0 -34
- package/skills/skill-grant-application-drafter/bin/cli.ts +0 -34
- package/skills/skill-grocery-basket-optimizer/bin/cli.ts +0 -34
- package/skills/skill-guest-communication-suite/bin/cli.ts +0 -34
- package/skills/skill-habit-reflection-digest/bin/cli.ts +0 -34
- package/skills/skill-highlight-reel-generator/bin/cli.ts +0 -34
- package/skills/skill-homework-feedback-coach/bin/cli.ts +0 -34
- package/skills/skill-household-maintenance-mgr/bin/cli.ts +0 -34
- package/skills/skill-http-server/bin/cli.ts +0 -34
- package/skills/skill-implementation-agent/bin/cli.ts +0 -34
- package/skills/skill-implementation-plan/bin/cli.ts +0 -34
- package/skills/skill-implementation-todo/bin/cli.ts +0 -34
- package/skills/skill-inbox-priority-planner/bin/cli.ts +0 -34
- package/skills/skill-invoice/bin/cli.ts +0 -20
- package/skills/skill-invoice-dispute-helper/bin/cli.ts +0 -34
- package/skills/skill-itinerary-architect/bin/cli.ts +0 -34
- package/skills/skill-jingle-composer/bin/cli.ts +0 -34
- package/skills/skill-kpi-digest-generator/bin/cli.ts +0 -34
- package/skills/skill-lab-notebook-formatter/bin/cli.ts +0 -34
- package/skills/skill-landing-page-copy/bin/cli.ts +0 -34
- package/skills/skill-latex-table-generator/bin/cli.ts +0 -34
- package/skills/skill-learning-style-profiler/bin/cli.ts +0 -34
- package/skills/skill-lesson-plan-customizer/bin/cli.ts +0 -34
- package/skills/skill-livestream-runofshow/bin/cli.ts +0 -34
- package/skills/skill-longform-structurer/bin/cli.ts +0 -34
- package/skills/skill-lorem-generator/bin/cli.ts +0 -34
- package/skills/skill-managehook/bin/cli.ts +0 -241
- package/skills/skill-managemcp/bin/cli.ts +0 -241
- package/skills/skill-manageskill/bin/cli.ts +0 -241
- package/skills/skill-markdown-validator/bin/cli.ts +0 -34
- package/skills/skill-mcp-builder/bin/cli.ts +0 -34
- package/skills/skill-meal-plan-designer/bin/cli.ts +0 -34
- package/skills/skill-meeting-insight-summarizer/bin/cli.ts +0 -34
- package/skills/skill-merge-pdfs/bin/cli.ts +0 -34
- package/skills/skill-microcopy-generator/bin/cli.ts +0 -34
- package/skills/skill-mindfulness-prompt-cache/bin/cli.ts +0 -34
- package/skills/skill-notion-manager/bin/cli.ts +0 -34
- package/skills/skill-onboarding-sequence-builder/bin/cli.ts +0 -34
- package/skills/skill-onsite-ops-checklist/bin/cli.ts +0 -34
- package/skills/skill-outreach-cadence-designer/bin/cli.ts +0 -34
- package/skills/skill-packaging-concept-studio/bin/cli.ts +0 -34
- package/skills/skill-packing-plan-pro/bin/cli.ts +0 -34
- package/skills/skill-parent-teacher-brief/bin/cli.ts +0 -34
- package/skills/skill-partner-kit-assembler/bin/cli.ts +0 -34
- package/skills/skill-payroll-change-prepper/bin/cli.ts +0 -34
- package/skills/skill-persona-based-adwriter/bin/cli.ts +0 -34
- package/skills/skill-persona-generator/bin/cli.ts +0 -34
- package/skills/skill-personal-daily-ops/bin/cli.ts +0 -34
- package/skills/skill-pet-care-scheduler/bin/cli.ts +0 -34
- package/skills/skill-podcast-show-notes/bin/cli.ts +0 -34
- package/skills/skill-presentation-theme-maker/bin/cli.ts +0 -34
- package/skills/skill-press-release-drafter/bin/cli.ts +0 -34
- package/skills/skill-print-collateral-designer/bin/cli.ts +0 -34
- package/skills/skill-procurement-scorecard/bin/cli.ts +0 -34
- package/skills/skill-product-demo-script/bin/cli.ts +0 -34
- package/skills/skill-product-mockup/bin/cli.ts +0 -34
- package/skills/skill-project-retro-companion/bin/cli.ts +0 -34
- package/skills/skill-proposal-redline-advisor/bin/cli.ts +0 -34
- package/skills/skill-regex-tester/bin/cli.ts +0 -34
- package/skills/skill-remove-background/bin/cli.ts +0 -34
- package/skills/skill-risk-disclosure-kit/bin/cli.ts +0 -34
- package/skills/skill-roi-comparison-tool/bin/cli.ts +0 -34
- package/skills/skill-sales-call-recapper/bin/cli.ts +0 -34
- package/skills/skill-salescopy/bin/cli.ts +0 -20
- package/skills/skill-scaffold-project/bin/cli.ts +0 -34
- package/skills/skill-scholarship-tracker/bin/cli.ts +0 -34
- package/skills/skill-scientific-figure-check/bin/cli.ts +0 -34
- package/skills/skill-seating-chart-maker/bin/cli.ts +0 -34
- package/skills/skill-security-audit/bin/cli.ts +0 -34
- package/skills/skill-seo-brief-builder/bin/cli.ts +0 -34
- package/skills/skill-slack-assistant/bin/cli.ts +0 -34
- package/skills/skill-sleep-routine-analyzer/bin/cli.ts +0 -34
- package/skills/skill-social-media-kit/bin/cli.ts +0 -34
- package/skills/skill-split-pdf/bin/cli.ts +0 -34
- package/skills/skill-sponsorship-proposal-lab/bin/cli.ts +0 -34
- package/skills/skill-spreadsheet-cleanroom/bin/cli.ts +0 -34
- package/skills/skill-statistical-test-selector/bin/cli.ts +0 -34
- package/skills/skill-stress-relief-playbook/bin/cli.ts +0 -34
- package/skills/skill-study-guide-builder/bin/cli.ts +0 -34
- package/skills/skill-subscription-spend-watcher/bin/cli.ts +0 -34
- package/skills/skill-subtitle/bin/cli.ts +0 -20
- package/skills/skill-survey-insight-extractor/bin/cli.ts +0 -34
- package/skills/skill-terraform-generator/bin/cli.ts +0 -34
- package/skills/skill-testimonial-graphics/bin/cli.ts +0 -34
- package/skills/skill-timesheet/bin/cli.ts +0 -47
- package/skills/skill-travel-budget-balancer/bin/cli.ts +0 -34
- package/skills/skill-validate-config/bin/cli.ts +0 -34
- package/skills/skill-video-cut-suggester/bin/cli.ts +0 -34
- package/skills/skill-video-downloader/bin/cli.ts +0 -34
- package/skills/skill-video-thumbnail/bin/cli.ts +0 -34
- package/skills/skill-voiceover-casting-assistant/bin/cli.ts +0 -34
- package/skills/skill-watermark/bin/cli.ts +0 -34
- package/skills/skill-webcrawling/bin/cli.ts +0 -21
- package/skills/skill-webinar-script-coach/bin/cli.ts +0 -34
- package/skills/skill-wellness-progress-reporter/bin/cli.ts +0 -34
- package/skills/skill-workout-cycle-planner/bin/cli.ts +0 -34
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
4
|
+
import { dirname, resolve } from "path";
|
|
5
|
+
import { PDFDocument } from "pdf-lib";
|
|
6
|
+
|
|
7
|
+
const VERSION = "0.1.0";
|
|
8
|
+
const DEFAULT_MODEL = process.env.ANTHROPIC_MODEL || "claude-sonnet-4-20250514";
|
|
9
|
+
const DEFAULT_PROMPT =
|
|
10
|
+
"Extract the text, tables, and structured content from this PDF. Preserve headings, bullets, and table structure.";
|
|
11
|
+
const API_URL = process.env.ANTHROPIC_API_URL || "https://api.anthropic.com/v1/messages";
|
|
12
|
+
const MAX_CHUNK_SIZE = 20;
|
|
13
|
+
const MAX_REQUEST_BYTES = 32 * 1024 * 1024;
|
|
14
|
+
|
|
15
|
+
type OutputFormat = "json" | "markdown" | "text";
|
|
16
|
+
|
|
17
|
+
interface CliOptions {
|
|
18
|
+
input?: string;
|
|
19
|
+
pages?: string;
|
|
20
|
+
prompt: string;
|
|
21
|
+
format: OutputFormat;
|
|
22
|
+
model: string;
|
|
23
|
+
chunkSize: number;
|
|
24
|
+
maxTokens: number;
|
|
25
|
+
output?: string;
|
|
26
|
+
text: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface PdfChunkResult {
|
|
30
|
+
chunk: number;
|
|
31
|
+
pages: number[];
|
|
32
|
+
response: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface PdfResult {
|
|
36
|
+
input: string;
|
|
37
|
+
model: string;
|
|
38
|
+
prompt: string;
|
|
39
|
+
format: OutputFormat;
|
|
40
|
+
totalPages: number;
|
|
41
|
+
requestedPages: number[];
|
|
42
|
+
chunkSize: number;
|
|
43
|
+
chunks: PdfChunkResult[];
|
|
44
|
+
mergedText: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function printHelp(): void {
|
|
48
|
+
console.log(`skill-read-pdf v${VERSION}
|
|
49
|
+
|
|
50
|
+
USAGE:
|
|
51
|
+
skill-read-pdf --input <path-or-url> [options]
|
|
52
|
+
|
|
53
|
+
OPTIONS:
|
|
54
|
+
-i, --input <path-or-url> PDF file path or remote URL
|
|
55
|
+
--pages <ranges> Page ranges like 1-3,5,8-10
|
|
56
|
+
-p, --prompt <text> Extraction prompt
|
|
57
|
+
-f, --format <value> json | markdown | text
|
|
58
|
+
-m, --model <name> Anthropic model to call
|
|
59
|
+
--chunk-size <n> Pages per request (max 20)
|
|
60
|
+
--max-tokens <n> Maximum response tokens per chunk
|
|
61
|
+
-o, --output <path> Save result to a file
|
|
62
|
+
--text Print only merged chunk text
|
|
63
|
+
--help Show this help message
|
|
64
|
+
--version Show the current version
|
|
65
|
+
`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseArgs(argv: string[]): CliOptions {
|
|
69
|
+
const options: CliOptions = {
|
|
70
|
+
prompt: DEFAULT_PROMPT,
|
|
71
|
+
format: "markdown",
|
|
72
|
+
model: DEFAULT_MODEL,
|
|
73
|
+
chunkSize: MAX_CHUNK_SIZE,
|
|
74
|
+
maxTokens: 1600,
|
|
75
|
+
text: false,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
79
|
+
const arg = argv[i];
|
|
80
|
+
switch (arg) {
|
|
81
|
+
case "--help":
|
|
82
|
+
case "-h":
|
|
83
|
+
printHelp();
|
|
84
|
+
process.exit(0);
|
|
85
|
+
case "--version":
|
|
86
|
+
case "-v":
|
|
87
|
+
console.log(VERSION);
|
|
88
|
+
process.exit(0);
|
|
89
|
+
case "--input":
|
|
90
|
+
case "-i":
|
|
91
|
+
options.input = argv[++i];
|
|
92
|
+
break;
|
|
93
|
+
case "--pages":
|
|
94
|
+
options.pages = argv[++i];
|
|
95
|
+
break;
|
|
96
|
+
case "--prompt":
|
|
97
|
+
case "-p":
|
|
98
|
+
options.prompt = argv[++i] ?? DEFAULT_PROMPT;
|
|
99
|
+
break;
|
|
100
|
+
case "--format":
|
|
101
|
+
case "-f": {
|
|
102
|
+
const value = (argv[++i] ?? "markdown").toLowerCase();
|
|
103
|
+
if (value !== "json" && value !== "markdown" && value !== "text") {
|
|
104
|
+
throw new Error(`Invalid --format value: ${value}`);
|
|
105
|
+
}
|
|
106
|
+
options.format = value;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "--model":
|
|
110
|
+
case "-m":
|
|
111
|
+
options.model = argv[++i] ?? DEFAULT_MODEL;
|
|
112
|
+
break;
|
|
113
|
+
case "--chunk-size": {
|
|
114
|
+
const value = Number.parseInt(argv[++i] ?? "", 10);
|
|
115
|
+
if (!Number.isFinite(value) || value <= 0 || value > MAX_CHUNK_SIZE) {
|
|
116
|
+
throw new Error(`--chunk-size must be between 1 and ${MAX_CHUNK_SIZE}`);
|
|
117
|
+
}
|
|
118
|
+
options.chunkSize = value;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "--max-tokens": {
|
|
122
|
+
const value = Number.parseInt(argv[++i] ?? "", 10);
|
|
123
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
124
|
+
throw new Error(`Invalid --max-tokens value: ${argv[i]}`);
|
|
125
|
+
}
|
|
126
|
+
options.maxTokens = value;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "--output":
|
|
130
|
+
case "-o":
|
|
131
|
+
options.output = argv[++i];
|
|
132
|
+
break;
|
|
133
|
+
case "--text":
|
|
134
|
+
options.text = true;
|
|
135
|
+
break;
|
|
136
|
+
default:
|
|
137
|
+
if (arg.startsWith("-")) {
|
|
138
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
139
|
+
}
|
|
140
|
+
if (!options.input) {
|
|
141
|
+
options.input = arg;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!options.input) {
|
|
149
|
+
throw new Error("Missing required --input <path-or-url> argument");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return options;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isUrl(value: string): boolean {
|
|
156
|
+
try {
|
|
157
|
+
const url = new URL(value);
|
|
158
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function loadPdfBytes(input: string): Promise<{ input: string; bytes: Uint8Array }> {
|
|
165
|
+
if (isUrl(input)) {
|
|
166
|
+
const response = await fetch(input);
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error(`Failed to download PDF: ${response.status} ${response.statusText}`);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
input,
|
|
172
|
+
bytes: new Uint8Array(await response.arrayBuffer()),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const resolvedInput = resolve(input);
|
|
177
|
+
return {
|
|
178
|
+
input: resolvedInput,
|
|
179
|
+
bytes: await readFile(resolvedInput),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function parsePageRanges(spec: string | undefined, totalPages: number): number[] {
|
|
184
|
+
if (!spec) {
|
|
185
|
+
return Array.from({ length: totalPages }, (_, index) => index + 1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const pages = new Set<number>();
|
|
189
|
+
for (const part of spec.split(",")) {
|
|
190
|
+
const trimmed = part.trim();
|
|
191
|
+
if (!trimmed) continue;
|
|
192
|
+
if (trimmed.includes("-")) {
|
|
193
|
+
const [startText, endText] = trimmed.split("-");
|
|
194
|
+
const start = Number.parseInt(startText ?? "", 10);
|
|
195
|
+
const end = Number.parseInt(endText ?? "", 10);
|
|
196
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start <= 0 || end <= 0 || end < start) {
|
|
197
|
+
throw new Error(`Invalid page range: ${trimmed}`);
|
|
198
|
+
}
|
|
199
|
+
for (let page = start; page <= end; page += 1) {
|
|
200
|
+
if (page > totalPages) {
|
|
201
|
+
throw new Error(`Requested page ${page} exceeds total pages (${totalPages})`);
|
|
202
|
+
}
|
|
203
|
+
pages.add(page);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const page = Number.parseInt(trimmed, 10);
|
|
209
|
+
if (!Number.isFinite(page) || page <= 0 || page > totalPages) {
|
|
210
|
+
throw new Error(`Invalid page number: ${trimmed}`);
|
|
211
|
+
}
|
|
212
|
+
pages.add(page);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return Array.from(pages).sort((a, b) => a - b);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function chunkPages(pages: number[], size: number): number[][] {
|
|
219
|
+
const chunks: number[][] = [];
|
|
220
|
+
for (let index = 0; index < pages.length; index += size) {
|
|
221
|
+
chunks.push(pages.slice(index, index + size));
|
|
222
|
+
}
|
|
223
|
+
return chunks;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function buildChunkPdf(sourcePdf: PDFDocument, pages: number[]): Promise<Uint8Array> {
|
|
227
|
+
const output = await PDFDocument.create();
|
|
228
|
+
const copiedPages = await output.copyPages(sourcePdf, pages.map((page) => page - 1));
|
|
229
|
+
for (const page of copiedPages) {
|
|
230
|
+
output.addPage(page);
|
|
231
|
+
}
|
|
232
|
+
return output.save();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function formatInstruction(format: OutputFormat): string {
|
|
236
|
+
switch (format) {
|
|
237
|
+
case "json":
|
|
238
|
+
return "Return valid JSON with keys summary, text, tables, and structure.";
|
|
239
|
+
case "text":
|
|
240
|
+
return "Return plain text only. Keep the important structure readable.";
|
|
241
|
+
case "markdown":
|
|
242
|
+
default:
|
|
243
|
+
return "Return markdown. Preserve headings, lists, and tables when possible.";
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function callAnthropicPdf(
|
|
248
|
+
pdfBytes: Uint8Array,
|
|
249
|
+
model: string,
|
|
250
|
+
maxTokens: number,
|
|
251
|
+
prompt: string,
|
|
252
|
+
): Promise<{ raw: unknown; text: string }> {
|
|
253
|
+
if (pdfBytes.byteLength > MAX_REQUEST_BYTES) {
|
|
254
|
+
throw new Error(`Chunk exceeds 32MB request limit (${pdfBytes.byteLength} bytes)`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
258
|
+
if (!apiKey) {
|
|
259
|
+
throw new Error("Missing ANTHROPIC_API_KEY");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const response = await fetch(API_URL, {
|
|
263
|
+
method: "POST",
|
|
264
|
+
headers: {
|
|
265
|
+
"content-type": "application/json",
|
|
266
|
+
"x-api-key": apiKey,
|
|
267
|
+
"anthropic-version": "2023-06-01",
|
|
268
|
+
},
|
|
269
|
+
body: JSON.stringify({
|
|
270
|
+
model,
|
|
271
|
+
max_tokens: maxTokens,
|
|
272
|
+
messages: [
|
|
273
|
+
{
|
|
274
|
+
role: "user",
|
|
275
|
+
content: [
|
|
276
|
+
{
|
|
277
|
+
type: "document",
|
|
278
|
+
source: {
|
|
279
|
+
type: "base64",
|
|
280
|
+
media_type: "application/pdf",
|
|
281
|
+
data: Buffer.from(pdfBytes).toString("base64"),
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
type: "text",
|
|
286
|
+
text: prompt,
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const payload = await response.json() as {
|
|
295
|
+
content?: Array<{ type: string; text?: string }>;
|
|
296
|
+
error?: { message?: string };
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
throw new Error(payload.error?.message || `Anthropic request failed with status ${response.status}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const text = (payload.content ?? [])
|
|
304
|
+
.filter((entry) => entry.type === "text" && typeof entry.text === "string")
|
|
305
|
+
.map((entry) => entry.text)
|
|
306
|
+
.join("\n\n");
|
|
307
|
+
|
|
308
|
+
return { raw: payload, text };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function writeOutput(path: string, value: string): Promise<void> {
|
|
312
|
+
await mkdir(dirname(path), { recursive: true });
|
|
313
|
+
await writeFile(path, value, "utf8");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function main(): Promise<void> {
|
|
317
|
+
const options = parseArgs(process.argv.slice(2));
|
|
318
|
+
const { input, bytes } = await loadPdfBytes(options.input!);
|
|
319
|
+
const sourcePdf = await PDFDocument.load(bytes);
|
|
320
|
+
const totalPages = sourcePdf.getPageCount();
|
|
321
|
+
const requestedPages = parsePageRanges(options.pages, totalPages);
|
|
322
|
+
const pageChunks = chunkPages(requestedPages, options.chunkSize);
|
|
323
|
+
const chunkResults: PdfChunkResult[] = [];
|
|
324
|
+
const mergedParts: string[] = [];
|
|
325
|
+
|
|
326
|
+
for (let index = 0; index < pageChunks.length; index += 1) {
|
|
327
|
+
const pages = pageChunks[index];
|
|
328
|
+
const chunkPdf = await buildChunkPdf(sourcePdf, pages);
|
|
329
|
+
const prompt = [
|
|
330
|
+
options.prompt,
|
|
331
|
+
formatInstruction(options.format),
|
|
332
|
+
`This chunk covers pages ${pages.join(", ")}.`,
|
|
333
|
+
`Chunk ${index + 1} of ${pageChunks.length}.`,
|
|
334
|
+
].join("\n\n");
|
|
335
|
+
const response = await callAnthropicPdf(chunkPdf, options.model, options.maxTokens, prompt);
|
|
336
|
+
chunkResults.push({
|
|
337
|
+
chunk: index + 1,
|
|
338
|
+
pages,
|
|
339
|
+
response: options.format === "json" ? tryParseJson(response.text) : response.text,
|
|
340
|
+
});
|
|
341
|
+
mergedParts.push(response.text);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const result: PdfResult = {
|
|
345
|
+
input,
|
|
346
|
+
model: options.model,
|
|
347
|
+
prompt: options.prompt,
|
|
348
|
+
format: options.format,
|
|
349
|
+
totalPages,
|
|
350
|
+
requestedPages,
|
|
351
|
+
chunkSize: options.chunkSize,
|
|
352
|
+
chunks: chunkResults,
|
|
353
|
+
mergedText: mergedParts.join("\n\n"),
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const output = options.text ? `${result.mergedText}\n` : `${JSON.stringify(result, null, 2)}\n`;
|
|
357
|
+
if (options.output) {
|
|
358
|
+
await writeOutput(resolve(options.output), output);
|
|
359
|
+
} else {
|
|
360
|
+
process.stdout.write(output);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function tryParseJson(value: string): unknown {
|
|
365
|
+
try {
|
|
366
|
+
return JSON.parse(value);
|
|
367
|
+
} catch {
|
|
368
|
+
return value;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
main().catch((error) => {
|
|
373
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
374
|
+
process.stderr.write(`skill-read-pdf: ${message}\n`);
|
|
375
|
+
process.exit(1);
|
|
376
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"noEmit": true
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"src/**/*"
|
|
12
|
+
],
|
|
13
|
+
"exclude": [
|
|
14
|
+
"node_modules",
|
|
15
|
+
"dist"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-tmux-session
|
|
3
|
+
description: Create and manage grouped tmux sessions where each session focuses on its own window and its own folder. Knows workspace layout, naming conventions, and both multi-project and multi-agent patterns.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# skill-tmux-session
|
|
8
|
+
|
|
9
|
+
Use this skill when you need to add a window and session to an existing group, or create a new group from scratch. These are grouped tmux sessions where every named session locks onto its own window so `tmux attach -t <name>` lands directly in the intended project or tool.
|
|
10
|
+
|
|
11
|
+
## Workspace Layout
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
~/workspace/ Linux (spark01, spark02) — always lowercase
|
|
15
|
+
~/Workspace/ macOS (apple01) — always capital W
|
|
16
|
+
|
|
17
|
+
[workspace]/
|
|
18
|
+
└── [division]/
|
|
19
|
+
└── [area]/
|
|
20
|
+
└── [area][status]/
|
|
21
|
+
└── [prefix]-[name]/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
workspace/hasna/opensource/opensourcedev/open-todos
|
|
28
|
+
workspace/hasna/opensource/opensourcedev/open-conversations
|
|
29
|
+
workspace/hasnaxyz/agent/agentdev/agent-claude
|
|
30
|
+
workspace/hasnaxyz/service/servicedev/service-chat
|
|
31
|
+
workspace/hasnastudio/hasnastudio-alumia/platform/platform-alumia
|
|
32
|
+
workspace/hasnaxyz/internalapp/iapp-takumi
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Local tools live under `~/local/local-[name]/`.
|
|
36
|
+
|
|
37
|
+
## How grouped sessions work
|
|
38
|
+
|
|
39
|
+
- A source session owns the windows.
|
|
40
|
+
- Named sessions join that source session and lock onto one window index.
|
|
41
|
+
- `tmux attach -t open-economy` should land in the `open-economy` window without manual switching.
|
|
42
|
+
|
|
43
|
+
## When to open a session
|
|
44
|
+
|
|
45
|
+
| Work type | Source session | Session/window naming |
|
|
46
|
+
|-----------|----------------|-----------------------|
|
|
47
|
+
| Multiple OSS repos | `opensourcemaintain` | `open-[name]` |
|
|
48
|
+
| Multiple agent repos | `agentdev` | `agent-[name]` |
|
|
49
|
+
| Multiple local tools | `local` | `local-[name]` |
|
|
50
|
+
| Multiple workers on one project | project name | `[project]-01`, `[project]-02`, ... |
|
|
51
|
+
|
|
52
|
+
Rule: tmux groups live at the area/team level, not the individual repo level.
|
|
53
|
+
|
|
54
|
+
## Add a window to an existing group
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
SOURCE="opensourcemaintain"
|
|
58
|
+
NAME="open-economy"
|
|
59
|
+
DIR="$HOME/workspace/hasna/opensource/opensourcedev/open-economy"
|
|
60
|
+
|
|
61
|
+
tmux new-window -t "$SOURCE" -n "$NAME" -c "$DIR"
|
|
62
|
+
IDX=$(tmux list-windows -t "$SOURCE" -F "#{window_index}:#{window_name}" | grep ":${NAME}$" | cut -d: -f1)
|
|
63
|
+
tmux new-session -d -s "$NAME" -t "$SOURCE"
|
|
64
|
+
tmux select-window -t "${NAME}:${IDX}"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Create a new group
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
SOURCE="opensourcemaintain"
|
|
71
|
+
BASE="$HOME/workspace/hasna/opensource/opensourcedev"
|
|
72
|
+
|
|
73
|
+
WINDOWS=(
|
|
74
|
+
"open-todos:$BASE/open-todos"
|
|
75
|
+
"open-conversations:$BASE/open-conversations"
|
|
76
|
+
"open-mementos:$BASE/open-mementos"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
tmux new-session -d -s "$SOURCE" -n "open-todos" -c "$BASE/open-todos"
|
|
80
|
+
|
|
81
|
+
for entry in "${WINDOWS[@]:1}"; do
|
|
82
|
+
tmux new-window -t "$SOURCE" -n "$(echo "$entry" | cut -d: -f1)" -c "$(echo "$entry" | cut -d: -f2)"
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
for entry in "${WINDOWS[@]}"; do
|
|
86
|
+
name=$(echo "$entry" | cut -d: -f1)
|
|
87
|
+
idx=$(tmux list-windows -t "$SOURCE" -F "#{window_index}:#{window_name}" | grep ":${name}$" | cut -d: -f1)
|
|
88
|
+
tmux new-session -d -s "$name" -t "$SOURCE"
|
|
89
|
+
tmux select-window -t "${name}:${idx}"
|
|
90
|
+
done
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Naming rules
|
|
94
|
+
|
|
95
|
+
- OSS project: `open-[name]`
|
|
96
|
+
- Agent repo: `agent-[name]`
|
|
97
|
+
- Service repo: `service-[name]`
|
|
98
|
+
- Local tool: `local-[name]`
|
|
99
|
+
- Multi-agent on one project: `[project]-01..N`
|
|
100
|
+
|
|
101
|
+
Always use the full prefixed name for both the window name and the session name.
|
|
102
|
+
|
|
103
|
+
## Verification
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
tmux display-message -t open-economy -p "#{window_index}:#{window_name} @ #{pane_current_path}"
|
|
107
|
+
tmux list-sessions | grep "(group opensourcemaintain)"
|
|
108
|
+
tmux list-panes -a -F "#{session_name}:#{window_name} dead=#{pane_dead}" | grep "dead=1"
|
|
109
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasnaxyz/skill-tmux-session",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Instruction-set skill for creating grouped tmux sessions with repo-aware naming",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"skill-tmux-session": "src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"tsconfig.json"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "restricted"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"tmux",
|
|
20
|
+
"sessions",
|
|
21
|
+
"workspace",
|
|
22
|
+
"automation",
|
|
23
|
+
"skill"
|
|
24
|
+
],
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "bun run src/index.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/bun": "latest",
|
|
32
|
+
"typescript": "^5.7.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
const VERSION = "0.1.0";
|
|
4
|
+
|
|
5
|
+
function printHelp(): void {
|
|
6
|
+
console.log(`skill-tmux-session - Instruction-set skill
|
|
7
|
+
|
|
8
|
+
DESCRIPTION:
|
|
9
|
+
Guides an agent through grouped tmux session setup with workspace-aware naming:
|
|
10
|
+
- create source sessions
|
|
11
|
+
- add windows for repos or local tools
|
|
12
|
+
- attach named sessions to the correct window index
|
|
13
|
+
- verify pane focus and dead sessions
|
|
14
|
+
|
|
15
|
+
USAGE:
|
|
16
|
+
skills docs tmux-session
|
|
17
|
+
skills install tmux-session --for claude
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
|
|
23
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
24
|
+
console.log(VERSION);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
29
|
+
printHelp();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log("This is an instruction-set skill. Install it for an agent with:");
|
|
34
|
+
console.log(" skills install tmux-session --for claude");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"noEmit": true
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"src/**/*"
|
|
12
|
+
],
|
|
13
|
+
"exclude": [
|
|
14
|
+
"node_modules",
|
|
15
|
+
"dist"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -1,34 +0,0 @@
|
|
|
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();
|
|
@@ -1,34 +0,0 @@
|
|
|
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-action-item-router");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const program = new Command();
|
|
12
|
-
|
|
13
|
-
program
|
|
14
|
-
.name("skill-action-item-router")
|
|
15
|
-
.description("Action Item Router 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();
|
|
@@ -1,34 +0,0 @@
|
|
|
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-ad-creative-generator");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const program = new Command();
|
|
12
|
-
|
|
13
|
-
program
|
|
14
|
-
.name("skill-ad-creative-generator")
|
|
15
|
-
.description("Ad Creative Generator 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();
|