@hasna/skills 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +136 -18
  2. package/bin/index.js +33877 -33924
  3. package/bin/mcp.js +196 -95
  4. package/dist/cli/commands/completion.d.ts +5 -0
  5. package/dist/cli/commands/create-sync-config.d.ts +5 -0
  6. package/dist/cli/commands/diagnostic.d.ts +5 -0
  7. package/dist/cli/commands/init.d.ts +5 -0
  8. package/dist/cli/commands/install.d.ts +5 -0
  9. package/dist/cli/commands/introspect.d.ts +5 -0
  10. package/dist/cli/commands/list.d.ts +5 -0
  11. package/dist/cli/commands/runtime.d.ts +5 -0
  12. package/dist/cli/commands/schedule.d.ts +5 -0
  13. package/dist/index.js +197 -97
  14. package/dist/lib/config.d.ts +1 -1
  15. package/dist/lib/registry.d.ts +1 -11
  16. package/dist/lib/scheduler.d.ts +1 -1
  17. package/dist/lib/scheduler.test.d.ts +4 -0
  18. package/dist/lib/search.d.ts +17 -0
  19. package/package.json +1 -1
  20. package/skills/skill-commitpush/SKILL.md +57 -0
  21. package/skills/skill-commitpush/package.json +34 -0
  22. package/skills/skill-commitpush/src/index.ts +34 -0
  23. package/skills/skill-commitpush/tsconfig.json +17 -0
  24. package/skills/skill-commitpushpr/SKILL.md +55 -0
  25. package/skills/skill-commitpushpr/package.json +34 -0
  26. package/skills/skill-commitpushpr/src/index.ts +34 -0
  27. package/skills/skill-commitpushpr/tsconfig.json +17 -0
  28. package/skills/skill-monitor/SKILL.md +69 -0
  29. package/skills/skill-monitor/package.json +34 -0
  30. package/skills/skill-monitor/src/index.ts +34 -0
  31. package/skills/skill-monitor/tsconfig.json +17 -0
  32. package/skills/skill-read-csv/SKILL.md +62 -0
  33. package/skills/skill-read-csv/package.json +38 -0
  34. package/skills/skill-read-csv/src/index.ts +331 -0
  35. package/skills/skill-read-csv/tsconfig.json +17 -0
  36. package/skills/skill-read-excel/SKILL.md +64 -0
  37. package/skills/skill-read-excel/package.json +37 -0
  38. package/skills/skill-read-excel/src/index.ts +253 -0
  39. package/skills/skill-read-excel/tsconfig.json +17 -0
  40. package/skills/skill-read-image/SKILL.md +47 -0
  41. package/skills/skill-read-image/package.json +34 -0
  42. package/skills/skill-read-image/src/index.ts +264 -0
  43. package/skills/skill-read-image/tsconfig.json +17 -0
  44. package/skills/skill-read-pdf/SKILL.md +52 -0
  45. package/skills/skill-read-pdf/package.json +37 -0
  46. package/skills/skill-read-pdf/src/index.ts +376 -0
  47. package/skills/skill-read-pdf/tsconfig.json +17 -0
  48. package/skills/skill-tmux-session/SKILL.md +109 -0
  49. package/skills/skill-tmux-session/package.json +34 -0
  50. package/skills/skill-tmux-session/src/index.ts +34 -0
  51. package/skills/skill-tmux-session/tsconfig.json +17 -0
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { mkdir, readFile, writeFile } from "fs/promises";
4
+ import { dirname, extname, resolve } from "path";
5
+
6
+ const VERSION = "0.1.0";
7
+ const DEFAULT_MODEL = process.env.ANTHROPIC_MODEL || "claude-sonnet-4-20250514";
8
+ const DEFAULT_PROMPT =
9
+ "Describe this image in detail. Extract any visible text, call out objects, layout, branding, and anything notable.";
10
+ const API_URL = process.env.ANTHROPIC_API_URL || "https://api.anthropic.com/v1/messages";
11
+
12
+ interface CliOptions {
13
+ input?: string;
14
+ prompt: string;
15
+ model: string;
16
+ maxTokens: number;
17
+ output?: string;
18
+ text: boolean;
19
+ }
20
+
21
+ interface ImageAnalysisResult {
22
+ input: string;
23
+ sourceType: "file" | "url";
24
+ model: string;
25
+ prompt: string;
26
+ analysis: string;
27
+ stopReason: string | null;
28
+ usage: unknown;
29
+ }
30
+
31
+ function printHelp(): void {
32
+ console.log(`skill-read-image v${VERSION}
33
+
34
+ USAGE:
35
+ skill-read-image --input <path-or-url> [options]
36
+
37
+ OPTIONS:
38
+ -i, --input <path-or-url> Local image path or remote URL
39
+ -p, --prompt <text> Extraction or analysis prompt
40
+ -m, --model <name> Anthropic model to call
41
+ --max-tokens <n> Maximum response tokens
42
+ -o, --output <path> Save result to a file
43
+ --text Print only Claude's text response
44
+ --help Show this help message
45
+ --version Show the current version
46
+ `);
47
+ }
48
+
49
+ function parseArgs(argv: string[]): CliOptions {
50
+ const options: CliOptions = {
51
+ prompt: DEFAULT_PROMPT,
52
+ model: DEFAULT_MODEL,
53
+ maxTokens: 1200,
54
+ text: false,
55
+ };
56
+
57
+ for (let i = 0; i < argv.length; i += 1) {
58
+ const arg = argv[i];
59
+ switch (arg) {
60
+ case "--help":
61
+ case "-h":
62
+ printHelp();
63
+ process.exit(0);
64
+ case "--version":
65
+ case "-v":
66
+ console.log(VERSION);
67
+ process.exit(0);
68
+ case "--input":
69
+ case "-i":
70
+ options.input = argv[++i];
71
+ break;
72
+ case "--prompt":
73
+ case "-p":
74
+ options.prompt = argv[++i] ?? DEFAULT_PROMPT;
75
+ break;
76
+ case "--model":
77
+ case "-m":
78
+ options.model = argv[++i] ?? DEFAULT_MODEL;
79
+ break;
80
+ case "--max-tokens": {
81
+ const value = Number.parseInt(argv[++i] ?? "", 10);
82
+ if (!Number.isFinite(value) || value <= 0) {
83
+ throw new Error(`Invalid --max-tokens value: ${argv[i]}`);
84
+ }
85
+ options.maxTokens = value;
86
+ break;
87
+ }
88
+ case "--output":
89
+ case "-o":
90
+ options.output = argv[++i];
91
+ break;
92
+ case "--text":
93
+ options.text = true;
94
+ break;
95
+ default:
96
+ if (arg.startsWith("-")) {
97
+ throw new Error(`Unknown option: ${arg}`);
98
+ }
99
+ if (!options.input) {
100
+ options.input = arg;
101
+ break;
102
+ }
103
+ throw new Error(`Unexpected argument: ${arg}`);
104
+ }
105
+ }
106
+
107
+ if (!options.input) {
108
+ throw new Error("Missing required --input <path-or-url> argument");
109
+ }
110
+
111
+ return options;
112
+ }
113
+
114
+ function isUrl(value: string): boolean {
115
+ try {
116
+ const url = new URL(value);
117
+ return url.protocol === "http:" || url.protocol === "https:";
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ function getImageMediaType(value: string): string {
124
+ const extension = extname(value).toLowerCase();
125
+ switch (extension) {
126
+ case ".jpg":
127
+ case ".jpeg":
128
+ return "image/jpeg";
129
+ case ".png":
130
+ return "image/png";
131
+ case ".gif":
132
+ return "image/gif";
133
+ case ".webp":
134
+ return "image/webp";
135
+ default:
136
+ throw new Error(`Unsupported image format: ${extension || value}`);
137
+ }
138
+ }
139
+
140
+ async function buildImageBlock(input: string): Promise<{ block: Record<string, unknown>; sourceType: "file" | "url"; resolvedInput: string }> {
141
+ if (isUrl(input)) {
142
+ return {
143
+ block: {
144
+ type: "image",
145
+ source: {
146
+ type: "url",
147
+ url: input,
148
+ },
149
+ },
150
+ sourceType: "url",
151
+ resolvedInput: input,
152
+ };
153
+ }
154
+
155
+ const resolvedInput = resolve(input);
156
+ const mediaType = getImageMediaType(resolvedInput);
157
+ const bytes = await readFile(resolvedInput);
158
+ return {
159
+ block: {
160
+ type: "image",
161
+ source: {
162
+ type: "base64",
163
+ media_type: mediaType,
164
+ data: Buffer.from(bytes).toString("base64"),
165
+ },
166
+ },
167
+ sourceType: "file",
168
+ resolvedInput,
169
+ };
170
+ }
171
+
172
+ async function callAnthropic(
173
+ model: string,
174
+ maxTokens: number,
175
+ prompt: string,
176
+ imageBlock: Record<string, unknown>,
177
+ ): Promise<{ analysis: string; usage: unknown; stopReason: string | null }> {
178
+ const apiKey = process.env.ANTHROPIC_API_KEY;
179
+ if (!apiKey) {
180
+ throw new Error("Missing ANTHROPIC_API_KEY");
181
+ }
182
+
183
+ const response = await fetch(API_URL, {
184
+ method: "POST",
185
+ headers: {
186
+ "content-type": "application/json",
187
+ "x-api-key": apiKey,
188
+ "anthropic-version": "2023-06-01",
189
+ },
190
+ body: JSON.stringify({
191
+ model,
192
+ max_tokens: maxTokens,
193
+ messages: [
194
+ {
195
+ role: "user",
196
+ content: [
197
+ imageBlock,
198
+ {
199
+ type: "text",
200
+ text: prompt,
201
+ },
202
+ ],
203
+ },
204
+ ],
205
+ }),
206
+ });
207
+
208
+ const payload = await response.json() as {
209
+ content?: Array<{ type: string; text?: string }>;
210
+ usage?: unknown;
211
+ stop_reason?: string | null;
212
+ error?: { message?: string };
213
+ };
214
+
215
+ if (!response.ok) {
216
+ throw new Error(payload.error?.message || `Anthropic request failed with status ${response.status}`);
217
+ }
218
+
219
+ const analysis = (payload.content ?? [])
220
+ .filter((entry) => entry.type === "text" && typeof entry.text === "string")
221
+ .map((entry) => entry.text)
222
+ .join("\n\n");
223
+
224
+ return {
225
+ analysis,
226
+ usage: payload.usage ?? null,
227
+ stopReason: payload.stop_reason ?? null,
228
+ };
229
+ }
230
+
231
+ async function writeOutput(path: string, value: string): Promise<void> {
232
+ await mkdir(dirname(path), { recursive: true });
233
+ await writeFile(path, value, "utf8");
234
+ }
235
+
236
+ async function main(): Promise<void> {
237
+ const options = parseArgs(process.argv.slice(2));
238
+ const { block, sourceType, resolvedInput } = await buildImageBlock(options.input!);
239
+ const response = await callAnthropic(options.model, options.maxTokens, options.prompt, block);
240
+
241
+ const result: ImageAnalysisResult = {
242
+ input: resolvedInput,
243
+ sourceType,
244
+ model: options.model,
245
+ prompt: options.prompt,
246
+ analysis: response.analysis,
247
+ stopReason: response.stopReason,
248
+ usage: response.usage,
249
+ };
250
+
251
+ const output = options.text ? `${response.analysis}\n` : `${JSON.stringify(result, null, 2)}\n`;
252
+
253
+ if (options.output) {
254
+ await writeOutput(resolve(options.output), output);
255
+ } else {
256
+ process.stdout.write(output);
257
+ }
258
+ }
259
+
260
+ main().catch((error) => {
261
+ const message = error instanceof Error ? error.message : String(error);
262
+ process.stderr.write(`skill-read-image: ${message}\n`);
263
+ process.exit(1);
264
+ });
@@ -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,52 @@
1
+ ---
2
+ name: skill-read-pdf
3
+ description: Analyze PDF files with Claude document blocks. Supports page-range selection, 20-page chunking, and merged structured output.
4
+ ---
5
+
6
+ # Read PDF
7
+
8
+ Analyze a PDF with Claude's native document support and return structured output for the requested pages.
9
+
10
+ ## Features
11
+
12
+ - Supports page range selection like `1-5,8,10-12`
13
+ - Chunks large selections into windows of up to 20 pages per request
14
+ - Emits merged JSON, markdown, or plain-text output
15
+ - Preserves chunk/page metadata so downstream tools can trace the source pages
16
+
17
+ ## Requirements
18
+
19
+ - `ANTHROPIC_API_KEY` must be available in the environment
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ # Read the whole PDF with markdown output
25
+ skill-read-pdf --input ./deck.pdf
26
+
27
+ # Restrict to a few pages
28
+ skill-read-pdf --input ./contract.pdf --pages 1-3,8 --format text
29
+
30
+ # Ask for specific structure and save it
31
+ skill-read-pdf \
32
+ --input ./invoice.pdf \
33
+ --prompt "Extract invoice metadata, every line item, and the payment terms." \
34
+ --format json \
35
+ --output ./invoice.json
36
+ ```
37
+
38
+ ## Options
39
+
40
+ | Option | Description | Default |
41
+ |--------|-------------|---------|
42
+ | `-i, --input <path-or-url>` | PDF file path or URL | required |
43
+ | `--pages <ranges>` | Page ranges like `1-5,8,10-12` | all pages |
44
+ | `-p, --prompt <text>` | What Claude should extract | text + tables + structure |
45
+ | `-f, --format <value>` | `json`, `markdown`, or `text` | `markdown` |
46
+ | `-m, --model <name>` | Anthropic model to call | `ANTHROPIC_MODEL` or built-in default |
47
+ | `--chunk-size <n>` | Pages per request (max 20) | 20 |
48
+ | `--max-tokens <n>` | Maximum response tokens per chunk | 1600 |
49
+ | `-o, --output <path>` | Save result to a file | stdout |
50
+ | `--text` | Emit only the merged extracted text | false |
51
+ | `--help` | Show usage | |
52
+ | `--version` | Show version | |
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@hasnaxyz/skill-read-pdf",
3
+ "version": "0.1.0",
4
+ "description": "Analyze PDFs with Claude document blocks and merge chunked output",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "bin": {
8
+ "skill-read-pdf": "src/index.ts"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "SKILL.md",
13
+ "tsconfig.json"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "restricted"
17
+ },
18
+ "keywords": [
19
+ "pdf",
20
+ "claude",
21
+ "document",
22
+ "ocr",
23
+ "skill"
24
+ ],
25
+ "license": "Apache-2.0",
26
+ "scripts": {
27
+ "start": "bun run src/index.ts",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "pdf-lib": "^1.17.1"
32
+ },
33
+ "devDependencies": {
34
+ "@types/bun": "latest",
35
+ "typescript": "^5.7.0"
36
+ }
37
+ }