@hasna/skills 0.1.19 → 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.
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
package/bin/mcp.js CHANGED
@@ -38746,7 +38746,7 @@ init_adapter();
38746
38746
  // package.json
38747
38747
  var package_default = {
38748
38748
  name: "@hasna/skills",
38749
- version: "0.1.19",
38749
+ version: "0.1.20",
38750
38750
  description: "Skills library for AI coding agents",
38751
38751
  type: "module",
38752
38752
  bin: {
@@ -38835,6 +38835,94 @@ var package_default = {
38835
38835
  import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync3 } from "fs";
38836
38836
  import { join as join5 } from "path";
38837
38837
  import { homedir as homedir6 } from "os";
38838
+
38839
+ // src/lib/search.ts
38840
+ function editDistance(a, b) {
38841
+ if (a === b)
38842
+ return 0;
38843
+ if (a.length === 0)
38844
+ return b.length;
38845
+ if (b.length === 0)
38846
+ return a.length;
38847
+ const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
38848
+ const curr = new Array(b.length + 1);
38849
+ for (let i = 1;i <= a.length; i++) {
38850
+ curr[0] = i;
38851
+ for (let j = 1;j <= b.length; j++) {
38852
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
38853
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
38854
+ }
38855
+ prev.splice(0, prev.length, ...curr);
38856
+ }
38857
+ return prev[b.length];
38858
+ }
38859
+ function fuzzyMatchScore(word, target) {
38860
+ if (target.includes(word))
38861
+ return 1;
38862
+ const tokens = target.split(/[\s\-_]+/).filter(Boolean);
38863
+ for (const token of tokens) {
38864
+ if (token.startsWith(word))
38865
+ return 0.6;
38866
+ }
38867
+ if (word.length >= 3) {
38868
+ const maxDist = word.length <= 3 ? 1 : 2;
38869
+ for (const token of tokens) {
38870
+ if (Math.abs(token.length - word.length) <= maxDist) {
38871
+ const dist = editDistance(word, token);
38872
+ if (dist <= maxDist)
38873
+ return 0.4;
38874
+ }
38875
+ }
38876
+ }
38877
+ return 0;
38878
+ }
38879
+ function searchSkills(query) {
38880
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
38881
+ if (words.length === 0)
38882
+ return [];
38883
+ const scored = [];
38884
+ for (const skill of loadRegistry()) {
38885
+ const nameLower = skill.name.toLowerCase();
38886
+ const displayNameLower = skill.displayName.toLowerCase();
38887
+ const descriptionLower = skill.description.toLowerCase();
38888
+ const tagsLower = skill.tags.map((t) => t.toLowerCase());
38889
+ const tagsCombined = tagsLower.join(" ");
38890
+ let score = 0;
38891
+ let allWordsMatch = true;
38892
+ for (const word of words) {
38893
+ let wordScore = 0;
38894
+ const nameMatch = fuzzyMatchScore(word, nameLower);
38895
+ if (nameMatch > 0)
38896
+ wordScore += 10 * nameMatch;
38897
+ const displayMatch = fuzzyMatchScore(word, displayNameLower);
38898
+ if (displayMatch > 0)
38899
+ wordScore += 7 * displayMatch;
38900
+ const tagMatch = Math.max(...tagsLower.map((t) => fuzzyMatchScore(word, t)), fuzzyMatchScore(word, tagsCombined));
38901
+ if (tagMatch > 0)
38902
+ wordScore += 5 * tagMatch;
38903
+ const descMatch = fuzzyMatchScore(word, descriptionLower);
38904
+ if (descMatch > 0)
38905
+ wordScore += 2 * descMatch;
38906
+ if (wordScore === 0) {
38907
+ allWordsMatch = false;
38908
+ break;
38909
+ }
38910
+ score += wordScore;
38911
+ }
38912
+ if (allWordsMatch && score > 0) {
38913
+ scored.push({ skill, score });
38914
+ }
38915
+ }
38916
+ scored.sort((a, b) => b.score - a.score);
38917
+ return scored.map((s) => s.skill);
38918
+ }
38919
+ function findSimilarSkills(query, maxResults = 3) {
38920
+ const q = query.toLowerCase();
38921
+ const scored = loadRegistry().map((s) => ({ name: s.name, dist: editDistance(q, s.name.toLowerCase()) })).filter((s) => s.dist <= Math.max(3, Math.floor(q.length / 2))).sort((a, b) => a.dist - b.dist);
38922
+ return scored.slice(0, maxResults).map((s) => s.name);
38923
+ }
38924
+
38925
+ // src/lib/registry.ts
38838
38926
  var CATEGORIES = [
38839
38927
  "Development Tools",
38840
38928
  "Business & Marketing",
@@ -38876,6 +38964,21 @@ var SKILLS = [
38876
38964
  category: "Development Tools",
38877
38965
  tags: ["code", "linting", "formatting", "quality"]
38878
38966
  },
38967
+ {
38968
+ name: "commitpush",
38969
+ displayName: "Commit Push",
38970
+ description: "Create logical commits from repo changes and push directly to the main branch",
38971
+ category: "Development Tools",
38972
+ tags: ["git", "commit", "push", "automation"]
38973
+ },
38974
+ {
38975
+ name: "commitpushpr",
38976
+ displayName: "Commit Push PR",
38977
+ description: "Create logical commits, push a feature branch, and open a GitHub pull request",
38978
+ category: "Development Tools",
38979
+ tags: ["git", "commit", "pull-request", "github", "automation"],
38980
+ dependencies: ["commitpush"]
38981
+ },
38879
38982
  {
38880
38983
  name: "consolelog",
38881
38984
  displayName: "Console Log",
@@ -39037,6 +39140,13 @@ var SKILLS = [
39037
39140
  category: "Development Tools",
39038
39141
  tags: ["mcp", "builder", "scaffold", "server"]
39039
39142
  },
39143
+ {
39144
+ name: "monitor",
39145
+ displayName: "Monitor",
39146
+ description: "Operate the open-monitor MCP for machine health, processes, cron jobs, and cleanup workflows",
39147
+ category: "Development Tools",
39148
+ tags: ["monitoring", "mcp", "processes", "operations"]
39149
+ },
39040
39150
  {
39041
39151
  name: "npmpublish",
39042
39152
  displayName: "NPM Publish",
@@ -39087,6 +39197,13 @@ var SKILLS = [
39087
39197
  category: "Development Tools",
39088
39198
  tags: ["terraform", "iac", "infrastructure", "devops"]
39089
39199
  },
39200
+ {
39201
+ name: "tmux-session",
39202
+ displayName: "Tmux Session",
39203
+ description: "Create and manage grouped tmux sessions with workspace-aware naming and window layout guidance",
39204
+ category: "Development Tools",
39205
+ tags: ["tmux", "terminal", "sessions", "workspace"]
39206
+ },
39090
39207
  {
39091
39208
  name: "validate-config",
39092
39209
  displayName: "Validate Config",
@@ -39689,6 +39806,34 @@ var SKILLS = [
39689
39806
  category: "Data & Analysis",
39690
39807
  tags: ["kpi", "digest", "metrics", "reporting"]
39691
39808
  },
39809
+ {
39810
+ name: "read-csv",
39811
+ displayName: "Read CSV",
39812
+ description: "Parse CSV files into structured JSON with delimiter and encoding detection",
39813
+ category: "Data & Analysis",
39814
+ tags: ["csv", "parsing", "tabular", "data"]
39815
+ },
39816
+ {
39817
+ name: "read-excel",
39818
+ displayName: "Read Excel",
39819
+ description: "Parse XLS and XLSX workbooks into structured JSON with sheet and formatted cell metadata",
39820
+ category: "Data & Analysis",
39821
+ tags: ["excel", "spreadsheet", "xlsx", "data"]
39822
+ },
39823
+ {
39824
+ name: "read-image",
39825
+ displayName: "Read Image",
39826
+ description: "Analyze local or remote images with Claude vision and extract visible text and visual structure",
39827
+ category: "Data & Analysis",
39828
+ tags: ["image", "vision", "ocr", "analysis"]
39829
+ },
39830
+ {
39831
+ name: "read-pdf",
39832
+ displayName: "Read PDF",
39833
+ description: "Extract text and structured content from PDF files with chunked Claude document analysis",
39834
+ category: "Data & Analysis",
39835
+ tags: ["pdf", "documents", "extraction", "analysis"]
39836
+ },
39692
39837
  {
39693
39838
  name: "spreadsheet-cleanroom",
39694
39839
  displayName: "Spreadsheet Cleanroom",
@@ -40370,103 +40515,9 @@ function loadRegistry(cwd) {
40370
40515
  function getSkillsByCategory(category) {
40371
40516
  return loadRegistry().filter((s) => s.category === category);
40372
40517
  }
40373
- function editDistance(a, b) {
40374
- if (a === b)
40375
- return 0;
40376
- if (a.length === 0)
40377
- return b.length;
40378
- if (b.length === 0)
40379
- return a.length;
40380
- const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
40381
- const curr = new Array(b.length + 1);
40382
- for (let i = 1;i <= a.length; i++) {
40383
- curr[0] = i;
40384
- for (let j = 1;j <= b.length; j++) {
40385
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
40386
- curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
40387
- }
40388
- prev.splice(0, prev.length, ...curr);
40389
- }
40390
- return prev[b.length];
40391
- }
40392
- function fuzzyMatchScore(word, target) {
40393
- if (target.includes(word))
40394
- return 1;
40395
- const tokens = target.split(/[\s\-_]+/).filter(Boolean);
40396
- for (const token of tokens) {
40397
- if (token.startsWith(word))
40398
- return 0.6;
40399
- }
40400
- if (word.length >= 3) {
40401
- const maxDist = word.length <= 3 ? 1 : 2;
40402
- for (const token of tokens) {
40403
- if (Math.abs(token.length - word.length) <= maxDist) {
40404
- const dist = editDistance(word, token);
40405
- if (dist <= maxDist)
40406
- return 0.4;
40407
- }
40408
- }
40409
- }
40410
- return 0;
40411
- }
40412
- function searchSkills(query) {
40413
- const words = query.toLowerCase().split(/\s+/).filter(Boolean);
40414
- if (words.length === 0)
40415
- return [];
40416
- const scored = [];
40417
- for (const skill of loadRegistry()) {
40418
- const nameLower = skill.name.toLowerCase();
40419
- const displayNameLower = skill.displayName.toLowerCase();
40420
- const descriptionLower = skill.description.toLowerCase();
40421
- const tagsLower = skill.tags.map((t) => t.toLowerCase());
40422
- const tagsCombined = tagsLower.join(" ");
40423
- let score = 0;
40424
- let allWordsMatch = true;
40425
- for (const word of words) {
40426
- let wordScore = 0;
40427
- const nameMatch = fuzzyMatchScore(word, nameLower);
40428
- if (nameMatch > 0)
40429
- wordScore += 10 * nameMatch;
40430
- const displayMatch = fuzzyMatchScore(word, displayNameLower);
40431
- if (displayMatch > 0)
40432
- wordScore += 7 * displayMatch;
40433
- const tagMatch = Math.max(...tagsLower.map((t) => fuzzyMatchScore(word, t)), fuzzyMatchScore(word, tagsCombined));
40434
- if (tagMatch > 0)
40435
- wordScore += 5 * tagMatch;
40436
- const descMatch = fuzzyMatchScore(word, descriptionLower);
40437
- if (descMatch > 0)
40438
- wordScore += 2 * descMatch;
40439
- if (wordScore === 0) {
40440
- allWordsMatch = false;
40441
- break;
40442
- }
40443
- score += wordScore;
40444
- }
40445
- if (allWordsMatch && score > 0) {
40446
- scored.push({ skill, score });
40447
- }
40448
- }
40449
- scored.sort((a, b) => b.score - a.score);
40450
- return scored.map((s) => s.skill);
40451
- }
40452
40518
  function getSkill(name) {
40453
40519
  return loadRegistry().find((s) => s.name === name);
40454
40520
  }
40455
- function levenshtein(a, b) {
40456
- const m = a.length, n = b.length;
40457
- const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0));
40458
- for (let i = 1;i <= m; i++) {
40459
- for (let j = 1;j <= n; j++) {
40460
- dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
40461
- }
40462
- }
40463
- return dp[m][n];
40464
- }
40465
- function findSimilarSkills(query, maxResults = 3) {
40466
- const q = query.toLowerCase();
40467
- const scored = loadRegistry().map((s) => ({ name: s.name, dist: levenshtein(q, s.name.toLowerCase()) })).filter((s) => s.dist <= Math.max(3, Math.floor(q.length / 2))).sort((a, b) => a.dist - b.dist);
40468
- return scored.slice(0, maxResults).map((s) => s.name);
40469
- }
40470
40521
 
40471
40522
  // src/lib/installer.ts
40472
40523
  import { existsSync as existsSync6, cpSync, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, rmSync, readdirSync as readdirSync5, statSync, readFileSync as readFileSync3, accessSync, constants } from "fs";
@@ -41063,11 +41114,61 @@ function saveSchedules(data, targetDir = process.cwd()) {
41063
41114
  mkdirSync5(dir, { recursive: true });
41064
41115
  writeFileSync3(path, JSON.stringify(data, null, 2));
41065
41116
  }
41117
+ function validateCronField(expr, min, max, label) {
41118
+ for (const part of expr.split(",")) {
41119
+ if (part === "*")
41120
+ continue;
41121
+ let valuePart = part;
41122
+ if (part.includes("/")) {
41123
+ const slashIdx = part.indexOf("/");
41124
+ valuePart = part.slice(0, slashIdx);
41125
+ const stepStr = part.slice(slashIdx + 1);
41126
+ const step = parseInt(stepStr);
41127
+ if (isNaN(step) || step < 1)
41128
+ return { valid: false, error: `Invalid step value in "${part}" in ${label}` };
41129
+ }
41130
+ if (valuePart === "*")
41131
+ continue;
41132
+ if (valuePart.includes("-")) {
41133
+ const rangeParts = valuePart.split("-");
41134
+ if (rangeParts.length !== 2)
41135
+ return { valid: false, error: `Invalid range expression "${valuePart}" in ${label}` };
41136
+ const lo = parseInt(rangeParts[0]);
41137
+ const hi = parseInt(rangeParts[1]);
41138
+ if (isNaN(lo) || isNaN(hi))
41139
+ return { valid: false, error: `Invalid range "${valuePart}" in ${label}` };
41140
+ if (lo < min || hi > max || lo > hi) {
41141
+ return { valid: false, error: `Range ${lo}-${hi} outside valid ${min}-${max} in ${label}` };
41142
+ }
41143
+ continue;
41144
+ }
41145
+ const n = parseInt(valuePart);
41146
+ if (isNaN(n))
41147
+ return { valid: false, error: `Invalid value "${valuePart}" in ${label}` };
41148
+ if (n < min || n > max) {
41149
+ return { valid: false, error: `Value ${n} outside valid ${min}-${max} in ${label}` };
41150
+ }
41151
+ }
41152
+ return { valid: true };
41153
+ }
41066
41154
  function validateCron(expr) {
41067
41155
  const fields = expr.trim().split(/\s+/);
41068
41156
  if (fields.length !== 5) {
41069
41157
  return { valid: false, error: `Expected 5 fields, got ${fields.length}. Format: "minute hour day-of-month month day-of-week"` };
41070
41158
  }
41159
+ const [minuteF, hourF, domF, monthF, dowF] = fields;
41160
+ const checks4 = [
41161
+ { expr: minuteF, min: 0, max: 59, label: "minute" },
41162
+ { expr: hourF, min: 0, max: 23, label: "hour" },
41163
+ { expr: domF, min: 1, max: 31, label: "day-of-month" },
41164
+ { expr: monthF, min: 1, max: 12, label: "month" },
41165
+ { expr: dowF, min: 0, max: 6, label: "day-of-week" }
41166
+ ];
41167
+ for (const { expr: f, min, max, label } of checks4) {
41168
+ const result = validateCronField(f, min, max, label);
41169
+ if (!result.valid)
41170
+ return result;
41171
+ }
41071
41172
  return { valid: true };
41072
41173
  }
41073
41174
  function getNextRun(cron, from = new Date) {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * completion — shell completion generation
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerCompletion(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * config / create / sync — configuration and scaffolding commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerCreateSync(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * test / doctor / auth / whoami / outdated — diagnostic commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerDiagnostic(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * init / export / import — project setup and sharing commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerSetup(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * install / remove / update — skill lifecycle commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerInstall(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * info / docs / requires / validate / diff — skill introspection commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerIntrospect(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * list / search / categories / tags — browsing commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerBrowse(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * run / mcp / serve / self-update — runtime commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerRuntime(parent: Command): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * schedule — cron-based skill scheduling commands
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerSchedule(parent: Command): void;