@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,64 @@
1
+ ---
2
+ name: skill-read-excel
3
+ description: Parse XLS and XLSX workbooks into structured JSON with sheet metadata, named ranges, and formatted cell snapshots.
4
+ ---
5
+
6
+ # Read Excel
7
+
8
+ Parse Excel workbooks from disk and return structured JSON for one or more sheets.
9
+
10
+ ## Features
11
+
12
+ - Supports `.xls` and `.xlsx`
13
+ - Reads multiple sheets in one run
14
+ - Returns workbook metadata, named ranges, and per-sheet rows
15
+ - Preserves sheet order and includes formatted cell snapshots
16
+ - Allows limiting rows for large workbooks
17
+
18
+ ## Usage
19
+
20
+ ```bash
21
+ # Parse all sheets
22
+ skill-read-excel --input ./forecast.xlsx
23
+
24
+ # Restrict to specific sheets
25
+ skill-read-excel --input ./ops.xls --sheets Summary,Costs
26
+
27
+ # Limit output and save it
28
+ skill-read-excel --input ./audit.xlsx --limit 100 --output ./audit.json
29
+ ```
30
+
31
+ ## Options
32
+
33
+ | Option | Description | Default |
34
+ |--------|-------------|---------|
35
+ | `-i, --input <path>` | Excel workbook to parse | required |
36
+ | `-s, --sheets <list>` | Comma-separated sheet names to include | all sheets |
37
+ | `-l, --limit <n>` | Limit rows returned per sheet | unlimited |
38
+ | `-o, --output <path>` | Save JSON result to a file | stdout |
39
+ | `--help` | Show usage | |
40
+ | `--version` | Show version | |
41
+
42
+ ## Output Shape
43
+
44
+ ```json
45
+ {
46
+ "input": "/absolute/path/to/workbook.xlsx",
47
+ "workbook": {
48
+ "sheetNames": ["Summary", "Data"],
49
+ "namedRanges": []
50
+ },
51
+ "sheets": [
52
+ {
53
+ "name": "Summary",
54
+ "index": 0,
55
+ "rowCount": 12,
56
+ "columnCount": 4,
57
+ "columns": ["month", "revenue", "cost"],
58
+ "rows": [
59
+ { "month": "Jan", "revenue": 1000, "cost": 600 }
60
+ ]
61
+ }
62
+ ]
63
+ }
64
+ ```
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@hasnaxyz/skill-read-excel",
3
+ "version": "0.1.0",
4
+ "description": "Parse XLS and XLSX workbooks into structured JSON",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "bin": {
8
+ "skill-read-excel": "src/index.ts"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "SKILL.md",
13
+ "tsconfig.json"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "restricted"
17
+ },
18
+ "keywords": [
19
+ "excel",
20
+ "xlsx",
21
+ "xls",
22
+ "spreadsheet",
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
+ "xlsx": "^0.18.5"
32
+ },
33
+ "devDependencies": {
34
+ "@types/bun": "latest",
35
+ "typescript": "^5.7.0"
36
+ }
37
+ }
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { mkdir, writeFile } from "fs/promises";
4
+ import { dirname, resolve } from "path";
5
+ import * as XLSX from "xlsx";
6
+
7
+ const VERSION = "0.1.0";
8
+
9
+ interface CliOptions {
10
+ input?: string;
11
+ sheets?: string[];
12
+ limit?: number;
13
+ output?: string;
14
+ }
15
+
16
+ interface NamedRange {
17
+ name: string;
18
+ ref: string;
19
+ sheet?: string;
20
+ }
21
+
22
+ interface SheetResult {
23
+ name: string;
24
+ index: number;
25
+ range: string | null;
26
+ rowCount: number;
27
+ columnCount: number;
28
+ columns: string[];
29
+ rows: Array<Record<string, unknown>>;
30
+ formattedCells: Array<{ cell: string; raw?: unknown; formatted?: string; format?: string }>;
31
+ }
32
+
33
+ interface WorkbookResult {
34
+ input: string;
35
+ workbook: {
36
+ sheetNames: string[];
37
+ namedRanges: NamedRange[];
38
+ };
39
+ sheets: SheetResult[];
40
+ }
41
+
42
+ function printHelp(): void {
43
+ console.log(`skill-read-excel v${VERSION}
44
+
45
+ USAGE:
46
+ skill-read-excel --input <path> [options]
47
+
48
+ OPTIONS:
49
+ -i, --input <path> Excel workbook to parse (.xls or .xlsx)
50
+ -s, --sheets <list> Comma-separated sheet names to include
51
+ -l, --limit <n> Limit rows returned per sheet
52
+ -o, --output <path> Save JSON result to a file
53
+ --help Show this help message
54
+ --version Show the current version
55
+ `);
56
+ }
57
+
58
+ function parseArgs(argv: string[]): CliOptions {
59
+ const options: CliOptions = {};
60
+
61
+ for (let i = 0; i < argv.length; i += 1) {
62
+ const arg = argv[i];
63
+ switch (arg) {
64
+ case "--help":
65
+ case "-h":
66
+ printHelp();
67
+ process.exit(0);
68
+ case "--version":
69
+ case "-v":
70
+ console.log(VERSION);
71
+ process.exit(0);
72
+ case "--input":
73
+ case "-i":
74
+ options.input = argv[++i];
75
+ break;
76
+ case "--sheets":
77
+ case "-s":
78
+ options.sheets = (argv[++i] ?? "")
79
+ .split(",")
80
+ .map((value) => value.trim())
81
+ .filter(Boolean);
82
+ break;
83
+ case "--limit":
84
+ case "-l": {
85
+ const value = Number.parseInt(argv[++i] ?? "", 10);
86
+ if (!Number.isFinite(value) || value <= 0) {
87
+ throw new Error(`Invalid --limit value: ${argv[i]}`);
88
+ }
89
+ options.limit = value;
90
+ break;
91
+ }
92
+ case "--output":
93
+ case "-o":
94
+ options.output = argv[++i];
95
+ break;
96
+ default:
97
+ if (arg.startsWith("-")) {
98
+ throw new Error(`Unknown option: ${arg}`);
99
+ }
100
+ if (!options.input) {
101
+ options.input = arg;
102
+ break;
103
+ }
104
+ throw new Error(`Unexpected argument: ${arg}`);
105
+ }
106
+ }
107
+
108
+ if (!options.input) {
109
+ throw new Error("Missing required --input <path> argument");
110
+ }
111
+
112
+ return options;
113
+ }
114
+
115
+ function normalizeColumnName(value: unknown, index: number, seen: Set<string>): string {
116
+ const text = String(value ?? "")
117
+ .trim()
118
+ .toLowerCase()
119
+ .replace(/[^a-z0-9]+/g, "_")
120
+ .replace(/^_+|_+$/g, "") || `column_${index + 1}`;
121
+
122
+ let next = text;
123
+ let suffix = 2;
124
+ while (seen.has(next)) {
125
+ next = `${text}_${suffix}`;
126
+ suffix += 1;
127
+ }
128
+ seen.add(next);
129
+ return next;
130
+ }
131
+
132
+ function inferHeader(rows: unknown[][]): boolean {
133
+ if (rows.length < 2) {
134
+ return false;
135
+ }
136
+ const first = rows[0];
137
+ const second = rows[1];
138
+ const unique = new Set(first.map((value) => String(value ?? "").trim().toLowerCase()).filter(Boolean));
139
+ const mostlyText = first.filter((value) => String(value ?? "").trim() !== "" && Number.isNaN(Number(value))).length >= Math.ceil(first.length / 2);
140
+ const secondHasNumeric = second.some((value) => String(value ?? "").trim() !== "" && !Number.isNaN(Number(value)));
141
+ return unique.size === first.length && mostlyText && secondHasNumeric;
142
+ }
143
+
144
+ function collectFormattedCells(sheet: XLSX.WorkSheet, maxCells = 50): Array<{ cell: string; raw?: unknown; formatted?: string; format?: string }> {
145
+ const result: Array<{ cell: string; raw?: unknown; formatted?: string; format?: string }> = [];
146
+ const entries = Object.entries(sheet).filter(([key]) => !key.startsWith("!"));
147
+
148
+ for (const [cell, value] of entries) {
149
+ if (result.length >= maxCells) {
150
+ break;
151
+ }
152
+ const cellValue = value as XLSX.CellObject;
153
+ if (cellValue.z || cellValue.w) {
154
+ result.push({
155
+ cell,
156
+ raw: cellValue.v,
157
+ formatted: cellValue.w,
158
+ format: typeof cellValue.z === "string" ? cellValue.z : undefined,
159
+ });
160
+ }
161
+ }
162
+
163
+ return result;
164
+ }
165
+
166
+ function parseSheet(name: string, index: number, sheet: XLSX.WorkSheet, limit?: number): SheetResult {
167
+ const rows = XLSX.utils.sheet_to_json(sheet, {
168
+ header: 1,
169
+ defval: null,
170
+ raw: false,
171
+ blankrows: false,
172
+ }) as unknown[][];
173
+
174
+ const trimmedRows = limit ? rows.slice(0, limit + 1) : rows;
175
+ const hasHeader = inferHeader(trimmedRows);
176
+ const headerRow = trimmedRows[0] ?? [];
177
+ const seen = new Set<string>();
178
+ const columns = (hasHeader ? headerRow : headerRow.map((_, colIndex) => `column_${colIndex + 1}`))
179
+ .map((value, colIndex) => normalizeColumnName(value, colIndex, seen));
180
+
181
+ const dataRows = hasHeader ? trimmedRows.slice(1) : trimmedRows;
182
+ const normalizedRows = dataRows.map((row) =>
183
+ columns.reduce<Record<string, unknown>>((record, column, colIndex) => {
184
+ record[column] = row[colIndex] ?? null;
185
+ return record;
186
+ }, {})
187
+ );
188
+
189
+ return {
190
+ name,
191
+ index,
192
+ range: sheet["!ref"] ?? null,
193
+ rowCount: normalizedRows.length,
194
+ columnCount: columns.length,
195
+ columns,
196
+ rows: normalizedRows,
197
+ formattedCells: collectFormattedCells(sheet),
198
+ };
199
+ }
200
+
201
+ function collectNamedRanges(workbook: XLSX.WorkBook): NamedRange[] {
202
+ const names = workbook.Workbook?.Names ?? [];
203
+ return names.map((entry) => ({
204
+ name: entry.Name ?? "",
205
+ ref: entry.Ref ?? "",
206
+ sheet: entry.Sheet != null ? workbook.SheetNames[entry.Sheet] : undefined,
207
+ }));
208
+ }
209
+
210
+ async function writeJson(path: string, payload: WorkbookResult): Promise<void> {
211
+ await mkdir(dirname(path), { recursive: true });
212
+ await writeFile(path, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
213
+ }
214
+
215
+ async function main(): Promise<void> {
216
+ const options = parseArgs(process.argv.slice(2));
217
+ const inputPath = resolve(options.input!);
218
+ const workbook = XLSX.readFile(inputPath, {
219
+ cellDates: true,
220
+ cellNF: true,
221
+ cellStyles: true,
222
+ dense: false,
223
+ });
224
+
225
+ const selectedSheetNames = options.sheets?.length
226
+ ? workbook.SheetNames.filter((name) => options.sheets!.includes(name))
227
+ : workbook.SheetNames;
228
+
229
+ if (selectedSheetNames.length === 0) {
230
+ throw new Error("No matching sheets found");
231
+ }
232
+
233
+ const result: WorkbookResult = {
234
+ input: inputPath,
235
+ workbook: {
236
+ sheetNames: workbook.SheetNames,
237
+ namedRanges: collectNamedRanges(workbook),
238
+ },
239
+ sheets: selectedSheetNames.map((name) => parseSheet(name, workbook.SheetNames.indexOf(name), workbook.Sheets[name], options.limit)),
240
+ };
241
+
242
+ if (options.output) {
243
+ await writeJson(resolve(options.output), result);
244
+ } else {
245
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
246
+ }
247
+ }
248
+
249
+ main().catch((error) => {
250
+ const message = error instanceof Error ? error.message : String(error);
251
+ process.stderr.write(`skill-read-excel: ${message}\n`);
252
+ process.exit(1);
253
+ });
@@ -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,47 @@
1
+ ---
2
+ name: skill-read-image
3
+ description: Analyze an image from a local path or URL with Claude vision. Supports prompt-controlled description, OCR, object counting, and brand/visual extraction.
4
+ ---
5
+
6
+ # Read Image
7
+
8
+ Analyze an image with Claude vision and return structured JSON containing the prompt, model, and extracted analysis.
9
+
10
+ ## Features
11
+
12
+ - Supports local files and remote URLs
13
+ - Accepts PNG, JPG, JPEG, GIF, and WEBP
14
+ - Prompt-controlled output for OCR, object counting, branding, layout analysis, and more
15
+ - Emits plain text or JSON
16
+
17
+ ## Requirements
18
+
19
+ - `ANTHROPIC_API_KEY` must be available in the environment
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ # Describe a local image
25
+ skill-read-image --input ./product-shot.jpg
26
+
27
+ # OCR a receipt from a URL
28
+ skill-read-image \
29
+ --input https://example.com/receipt.png \
30
+ --prompt "Extract every line item, subtotal, tax, and total."
31
+
32
+ # Return plain text only
33
+ skill-read-image --input ./wireframe.webp --text
34
+ ```
35
+
36
+ ## Options
37
+
38
+ | Option | Description | Default |
39
+ |--------|-------------|---------|
40
+ | `-i, --input <path-or-url>` | Local image path or image URL | required |
41
+ | `-p, --prompt <text>` | What Claude should extract or describe | detailed description + OCR |
42
+ | `-m, --model <name>` | Anthropic model to call | `ANTHROPIC_MODEL` or built-in default |
43
+ | `--max-tokens <n>` | Maximum response tokens | 1200 |
44
+ | `-o, --output <path>` | Save result to a file | stdout |
45
+ | `--text` | Emit only Claude's text response | false |
46
+ | `--help` | Show usage | |
47
+ | `--version` | Show version | |
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@hasnaxyz/skill-read-image",
3
+ "version": "0.1.0",
4
+ "description": "Analyze local or remote images with Claude vision",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "bin": {
8
+ "skill-read-image": "src/index.ts"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "SKILL.md",
13
+ "tsconfig.json"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "restricted"
17
+ },
18
+ "keywords": [
19
+ "image",
20
+ "vision",
21
+ "ocr",
22
+ "claude",
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
+ }