@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.
- package/README.md +136 -18
- package/bin/index.js +33877 -33924
- package/bin/mcp.js +196 -95
- 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 +1 -1
- 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/dist/index.js
CHANGED
|
@@ -3,6 +3,94 @@
|
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { homedir } from "os";
|
|
6
|
+
|
|
7
|
+
// src/lib/search.ts
|
|
8
|
+
function editDistance(a, b) {
|
|
9
|
+
if (a === b)
|
|
10
|
+
return 0;
|
|
11
|
+
if (a.length === 0)
|
|
12
|
+
return b.length;
|
|
13
|
+
if (b.length === 0)
|
|
14
|
+
return a.length;
|
|
15
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
16
|
+
const curr = new Array(b.length + 1);
|
|
17
|
+
for (let i = 1;i <= a.length; i++) {
|
|
18
|
+
curr[0] = i;
|
|
19
|
+
for (let j = 1;j <= b.length; j++) {
|
|
20
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
21
|
+
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
22
|
+
}
|
|
23
|
+
prev.splice(0, prev.length, ...curr);
|
|
24
|
+
}
|
|
25
|
+
return prev[b.length];
|
|
26
|
+
}
|
|
27
|
+
function fuzzyMatchScore(word, target) {
|
|
28
|
+
if (target.includes(word))
|
|
29
|
+
return 1;
|
|
30
|
+
const tokens = target.split(/[\s\-_]+/).filter(Boolean);
|
|
31
|
+
for (const token of tokens) {
|
|
32
|
+
if (token.startsWith(word))
|
|
33
|
+
return 0.6;
|
|
34
|
+
}
|
|
35
|
+
if (word.length >= 3) {
|
|
36
|
+
const maxDist = word.length <= 3 ? 1 : 2;
|
|
37
|
+
for (const token of tokens) {
|
|
38
|
+
if (Math.abs(token.length - word.length) <= maxDist) {
|
|
39
|
+
const dist = editDistance(word, token);
|
|
40
|
+
if (dist <= maxDist)
|
|
41
|
+
return 0.4;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
function searchSkills(query) {
|
|
48
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
49
|
+
if (words.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
const scored = [];
|
|
52
|
+
for (const skill of loadRegistry()) {
|
|
53
|
+
const nameLower = skill.name.toLowerCase();
|
|
54
|
+
const displayNameLower = skill.displayName.toLowerCase();
|
|
55
|
+
const descriptionLower = skill.description.toLowerCase();
|
|
56
|
+
const tagsLower = skill.tags.map((t) => t.toLowerCase());
|
|
57
|
+
const tagsCombined = tagsLower.join(" ");
|
|
58
|
+
let score = 0;
|
|
59
|
+
let allWordsMatch = true;
|
|
60
|
+
for (const word of words) {
|
|
61
|
+
let wordScore = 0;
|
|
62
|
+
const nameMatch = fuzzyMatchScore(word, nameLower);
|
|
63
|
+
if (nameMatch > 0)
|
|
64
|
+
wordScore += 10 * nameMatch;
|
|
65
|
+
const displayMatch = fuzzyMatchScore(word, displayNameLower);
|
|
66
|
+
if (displayMatch > 0)
|
|
67
|
+
wordScore += 7 * displayMatch;
|
|
68
|
+
const tagMatch = Math.max(...tagsLower.map((t) => fuzzyMatchScore(word, t)), fuzzyMatchScore(word, tagsCombined));
|
|
69
|
+
if (tagMatch > 0)
|
|
70
|
+
wordScore += 5 * tagMatch;
|
|
71
|
+
const descMatch = fuzzyMatchScore(word, descriptionLower);
|
|
72
|
+
if (descMatch > 0)
|
|
73
|
+
wordScore += 2 * descMatch;
|
|
74
|
+
if (wordScore === 0) {
|
|
75
|
+
allWordsMatch = false;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
score += wordScore;
|
|
79
|
+
}
|
|
80
|
+
if (allWordsMatch && score > 0) {
|
|
81
|
+
scored.push({ skill, score });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
scored.sort((a, b) => b.score - a.score);
|
|
85
|
+
return scored.map((s) => s.skill);
|
|
86
|
+
}
|
|
87
|
+
function findSimilarSkills(query, maxResults = 3) {
|
|
88
|
+
const q = query.toLowerCase();
|
|
89
|
+
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);
|
|
90
|
+
return scored.slice(0, maxResults).map((s) => s.name);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/lib/registry.ts
|
|
6
94
|
var CATEGORIES = [
|
|
7
95
|
"Development Tools",
|
|
8
96
|
"Business & Marketing",
|
|
@@ -44,6 +132,21 @@ var SKILLS = [
|
|
|
44
132
|
category: "Development Tools",
|
|
45
133
|
tags: ["code", "linting", "formatting", "quality"]
|
|
46
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: "commitpush",
|
|
137
|
+
displayName: "Commit Push",
|
|
138
|
+
description: "Create logical commits from repo changes and push directly to the main branch",
|
|
139
|
+
category: "Development Tools",
|
|
140
|
+
tags: ["git", "commit", "push", "automation"]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "commitpushpr",
|
|
144
|
+
displayName: "Commit Push PR",
|
|
145
|
+
description: "Create logical commits, push a feature branch, and open a GitHub pull request",
|
|
146
|
+
category: "Development Tools",
|
|
147
|
+
tags: ["git", "commit", "pull-request", "github", "automation"],
|
|
148
|
+
dependencies: ["commitpush"]
|
|
149
|
+
},
|
|
47
150
|
{
|
|
48
151
|
name: "consolelog",
|
|
49
152
|
displayName: "Console Log",
|
|
@@ -205,6 +308,13 @@ var SKILLS = [
|
|
|
205
308
|
category: "Development Tools",
|
|
206
309
|
tags: ["mcp", "builder", "scaffold", "server"]
|
|
207
310
|
},
|
|
311
|
+
{
|
|
312
|
+
name: "monitor",
|
|
313
|
+
displayName: "Monitor",
|
|
314
|
+
description: "Operate the open-monitor MCP for machine health, processes, cron jobs, and cleanup workflows",
|
|
315
|
+
category: "Development Tools",
|
|
316
|
+
tags: ["monitoring", "mcp", "processes", "operations"]
|
|
317
|
+
},
|
|
208
318
|
{
|
|
209
319
|
name: "npmpublish",
|
|
210
320
|
displayName: "NPM Publish",
|
|
@@ -255,6 +365,13 @@ var SKILLS = [
|
|
|
255
365
|
category: "Development Tools",
|
|
256
366
|
tags: ["terraform", "iac", "infrastructure", "devops"]
|
|
257
367
|
},
|
|
368
|
+
{
|
|
369
|
+
name: "tmux-session",
|
|
370
|
+
displayName: "Tmux Session",
|
|
371
|
+
description: "Create and manage grouped tmux sessions with workspace-aware naming and window layout guidance",
|
|
372
|
+
category: "Development Tools",
|
|
373
|
+
tags: ["tmux", "terminal", "sessions", "workspace"]
|
|
374
|
+
},
|
|
258
375
|
{
|
|
259
376
|
name: "validate-config",
|
|
260
377
|
displayName: "Validate Config",
|
|
@@ -857,6 +974,34 @@ var SKILLS = [
|
|
|
857
974
|
category: "Data & Analysis",
|
|
858
975
|
tags: ["kpi", "digest", "metrics", "reporting"]
|
|
859
976
|
},
|
|
977
|
+
{
|
|
978
|
+
name: "read-csv",
|
|
979
|
+
displayName: "Read CSV",
|
|
980
|
+
description: "Parse CSV files into structured JSON with delimiter and encoding detection",
|
|
981
|
+
category: "Data & Analysis",
|
|
982
|
+
tags: ["csv", "parsing", "tabular", "data"]
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
name: "read-excel",
|
|
986
|
+
displayName: "Read Excel",
|
|
987
|
+
description: "Parse XLS and XLSX workbooks into structured JSON with sheet and formatted cell metadata",
|
|
988
|
+
category: "Data & Analysis",
|
|
989
|
+
tags: ["excel", "spreadsheet", "xlsx", "data"]
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
name: "read-image",
|
|
993
|
+
displayName: "Read Image",
|
|
994
|
+
description: "Analyze local or remote images with Claude vision and extract visible text and visual structure",
|
|
995
|
+
category: "Data & Analysis",
|
|
996
|
+
tags: ["image", "vision", "ocr", "analysis"]
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
name: "read-pdf",
|
|
1000
|
+
displayName: "Read PDF",
|
|
1001
|
+
description: "Extract text and structured content from PDF files with chunked Claude document analysis",
|
|
1002
|
+
category: "Data & Analysis",
|
|
1003
|
+
tags: ["pdf", "documents", "extraction", "analysis"]
|
|
1004
|
+
},
|
|
860
1005
|
{
|
|
861
1006
|
name: "spreadsheet-cleanroom",
|
|
862
1007
|
displayName: "Spreadsheet Cleanroom",
|
|
@@ -1542,85 +1687,6 @@ function clearRegistryCache() {
|
|
|
1542
1687
|
function getSkillsByCategory(category) {
|
|
1543
1688
|
return loadRegistry().filter((s) => s.category === category);
|
|
1544
1689
|
}
|
|
1545
|
-
function editDistance(a, b) {
|
|
1546
|
-
if (a === b)
|
|
1547
|
-
return 0;
|
|
1548
|
-
if (a.length === 0)
|
|
1549
|
-
return b.length;
|
|
1550
|
-
if (b.length === 0)
|
|
1551
|
-
return a.length;
|
|
1552
|
-
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
1553
|
-
const curr = new Array(b.length + 1);
|
|
1554
|
-
for (let i = 1;i <= a.length; i++) {
|
|
1555
|
-
curr[0] = i;
|
|
1556
|
-
for (let j = 1;j <= b.length; j++) {
|
|
1557
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
1558
|
-
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
1559
|
-
}
|
|
1560
|
-
prev.splice(0, prev.length, ...curr);
|
|
1561
|
-
}
|
|
1562
|
-
return prev[b.length];
|
|
1563
|
-
}
|
|
1564
|
-
function fuzzyMatchScore(word, target) {
|
|
1565
|
-
if (target.includes(word))
|
|
1566
|
-
return 1;
|
|
1567
|
-
const tokens = target.split(/[\s\-_]+/).filter(Boolean);
|
|
1568
|
-
for (const token of tokens) {
|
|
1569
|
-
if (token.startsWith(word))
|
|
1570
|
-
return 0.6;
|
|
1571
|
-
}
|
|
1572
|
-
if (word.length >= 3) {
|
|
1573
|
-
const maxDist = word.length <= 3 ? 1 : 2;
|
|
1574
|
-
for (const token of tokens) {
|
|
1575
|
-
if (Math.abs(token.length - word.length) <= maxDist) {
|
|
1576
|
-
const dist = editDistance(word, token);
|
|
1577
|
-
if (dist <= maxDist)
|
|
1578
|
-
return 0.4;
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
return 0;
|
|
1583
|
-
}
|
|
1584
|
-
function searchSkills(query) {
|
|
1585
|
-
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1586
|
-
if (words.length === 0)
|
|
1587
|
-
return [];
|
|
1588
|
-
const scored = [];
|
|
1589
|
-
for (const skill of loadRegistry()) {
|
|
1590
|
-
const nameLower = skill.name.toLowerCase();
|
|
1591
|
-
const displayNameLower = skill.displayName.toLowerCase();
|
|
1592
|
-
const descriptionLower = skill.description.toLowerCase();
|
|
1593
|
-
const tagsLower = skill.tags.map((t) => t.toLowerCase());
|
|
1594
|
-
const tagsCombined = tagsLower.join(" ");
|
|
1595
|
-
let score = 0;
|
|
1596
|
-
let allWordsMatch = true;
|
|
1597
|
-
for (const word of words) {
|
|
1598
|
-
let wordScore = 0;
|
|
1599
|
-
const nameMatch = fuzzyMatchScore(word, nameLower);
|
|
1600
|
-
if (nameMatch > 0)
|
|
1601
|
-
wordScore += 10 * nameMatch;
|
|
1602
|
-
const displayMatch = fuzzyMatchScore(word, displayNameLower);
|
|
1603
|
-
if (displayMatch > 0)
|
|
1604
|
-
wordScore += 7 * displayMatch;
|
|
1605
|
-
const tagMatch = Math.max(...tagsLower.map((t) => fuzzyMatchScore(word, t)), fuzzyMatchScore(word, tagsCombined));
|
|
1606
|
-
if (tagMatch > 0)
|
|
1607
|
-
wordScore += 5 * tagMatch;
|
|
1608
|
-
const descMatch = fuzzyMatchScore(word, descriptionLower);
|
|
1609
|
-
if (descMatch > 0)
|
|
1610
|
-
wordScore += 2 * descMatch;
|
|
1611
|
-
if (wordScore === 0) {
|
|
1612
|
-
allWordsMatch = false;
|
|
1613
|
-
break;
|
|
1614
|
-
}
|
|
1615
|
-
score += wordScore;
|
|
1616
|
-
}
|
|
1617
|
-
if (allWordsMatch && score > 0) {
|
|
1618
|
-
scored.push({ skill, score });
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1622
|
-
return scored.map((s) => s.skill);
|
|
1623
|
-
}
|
|
1624
1690
|
function getSkill(name) {
|
|
1625
1691
|
return loadRegistry().find((s) => s.name === name);
|
|
1626
1692
|
}
|
|
@@ -1631,27 +1697,11 @@ function getSkillsByTag(tag) {
|
|
|
1631
1697
|
function getAllTags() {
|
|
1632
1698
|
const tagSet = new Set;
|
|
1633
1699
|
for (const skill of loadRegistry()) {
|
|
1634
|
-
for (const tag of skill.tags)
|
|
1700
|
+
for (const tag of skill.tags)
|
|
1635
1701
|
tagSet.add(tag.toLowerCase());
|
|
1636
|
-
}
|
|
1637
1702
|
}
|
|
1638
1703
|
return Array.from(tagSet).sort();
|
|
1639
1704
|
}
|
|
1640
|
-
function levenshtein(a, b) {
|
|
1641
|
-
const m = a.length, n = b.length;
|
|
1642
|
-
const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0));
|
|
1643
|
-
for (let i = 1;i <= m; i++) {
|
|
1644
|
-
for (let j = 1;j <= n; j++) {
|
|
1645
|
-
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]);
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
return dp[m][n];
|
|
1649
|
-
}
|
|
1650
|
-
function findSimilarSkills(query, maxResults = 3) {
|
|
1651
|
-
const q = query.toLowerCase();
|
|
1652
|
-
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);
|
|
1653
|
-
return scored.slice(0, maxResults).map((s) => s.name);
|
|
1654
|
-
}
|
|
1655
1705
|
// src/lib/installer.ts
|
|
1656
1706
|
import { existsSync as existsSync2, cpSync, mkdirSync, writeFileSync, rmSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync2, accessSync, constants } from "fs";
|
|
1657
1707
|
import { join as join2, dirname } from "path";
|
|
@@ -2223,7 +2273,7 @@ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync
|
|
|
2223
2273
|
import { join as join4, dirname as dirname2 } from "path";
|
|
2224
2274
|
import { homedir as homedir3 } from "os";
|
|
2225
2275
|
var VALID_KEYS = {
|
|
2226
|
-
defaultAgent: ["claude", "codex", "gemini", "all"],
|
|
2276
|
+
defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
|
|
2227
2277
|
defaultScope: ["global", "project"],
|
|
2228
2278
|
format: ["compact", "json", "csv"]
|
|
2229
2279
|
};
|
|
@@ -2322,11 +2372,61 @@ function saveSchedules(data, targetDir = process.cwd()) {
|
|
|
2322
2372
|
mkdirSync3(dir, { recursive: true });
|
|
2323
2373
|
writeFileSync3(path, JSON.stringify(data, null, 2));
|
|
2324
2374
|
}
|
|
2375
|
+
function validateCronField(expr, min, max, label) {
|
|
2376
|
+
for (const part of expr.split(",")) {
|
|
2377
|
+
if (part === "*")
|
|
2378
|
+
continue;
|
|
2379
|
+
let valuePart = part;
|
|
2380
|
+
if (part.includes("/")) {
|
|
2381
|
+
const slashIdx = part.indexOf("/");
|
|
2382
|
+
valuePart = part.slice(0, slashIdx);
|
|
2383
|
+
const stepStr = part.slice(slashIdx + 1);
|
|
2384
|
+
const step = parseInt(stepStr);
|
|
2385
|
+
if (isNaN(step) || step < 1)
|
|
2386
|
+
return { valid: false, error: `Invalid step value in "${part}" in ${label}` };
|
|
2387
|
+
}
|
|
2388
|
+
if (valuePart === "*")
|
|
2389
|
+
continue;
|
|
2390
|
+
if (valuePart.includes("-")) {
|
|
2391
|
+
const rangeParts = valuePart.split("-");
|
|
2392
|
+
if (rangeParts.length !== 2)
|
|
2393
|
+
return { valid: false, error: `Invalid range expression "${valuePart}" in ${label}` };
|
|
2394
|
+
const lo = parseInt(rangeParts[0]);
|
|
2395
|
+
const hi = parseInt(rangeParts[1]);
|
|
2396
|
+
if (isNaN(lo) || isNaN(hi))
|
|
2397
|
+
return { valid: false, error: `Invalid range "${valuePart}" in ${label}` };
|
|
2398
|
+
if (lo < min || hi > max || lo > hi) {
|
|
2399
|
+
return { valid: false, error: `Range ${lo}-${hi} outside valid ${min}-${max} in ${label}` };
|
|
2400
|
+
}
|
|
2401
|
+
continue;
|
|
2402
|
+
}
|
|
2403
|
+
const n = parseInt(valuePart);
|
|
2404
|
+
if (isNaN(n))
|
|
2405
|
+
return { valid: false, error: `Invalid value "${valuePart}" in ${label}` };
|
|
2406
|
+
if (n < min || n > max) {
|
|
2407
|
+
return { valid: false, error: `Value ${n} outside valid ${min}-${max} in ${label}` };
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return { valid: true };
|
|
2411
|
+
}
|
|
2325
2412
|
function validateCron(expr) {
|
|
2326
2413
|
const fields = expr.trim().split(/\s+/);
|
|
2327
2414
|
if (fields.length !== 5) {
|
|
2328
2415
|
return { valid: false, error: `Expected 5 fields, got ${fields.length}. Format: "minute hour day-of-month month day-of-week"` };
|
|
2329
2416
|
}
|
|
2417
|
+
const [minuteF, hourF, domF, monthF, dowF] = fields;
|
|
2418
|
+
const checks = [
|
|
2419
|
+
{ expr: minuteF, min: 0, max: 59, label: "minute" },
|
|
2420
|
+
{ expr: hourF, min: 0, max: 23, label: "hour" },
|
|
2421
|
+
{ expr: domF, min: 1, max: 31, label: "day-of-month" },
|
|
2422
|
+
{ expr: monthF, min: 1, max: 12, label: "month" },
|
|
2423
|
+
{ expr: dowF, min: 0, max: 6, label: "day-of-week" }
|
|
2424
|
+
];
|
|
2425
|
+
for (const { expr: f, min, max, label } of checks) {
|
|
2426
|
+
const result = validateCronField(f, min, max, label);
|
|
2427
|
+
if (!result.valid)
|
|
2428
|
+
return result;
|
|
2429
|
+
}
|
|
2330
2430
|
return { valid: true };
|
|
2331
2431
|
}
|
|
2332
2432
|
function getNextRun(cron, from = new Date) {
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Values from the project config override global config.
|
|
10
10
|
*/
|
|
11
11
|
export interface SkillsConfig {
|
|
12
|
-
defaultAgent?: "claude" | "codex" | "gemini" | "all";
|
|
12
|
+
defaultAgent?: "claude" | "codex" | "gemini" | "pi" | "opencode" | "all";
|
|
13
13
|
defaultScope?: "global" | "project";
|
|
14
14
|
format?: "compact" | "json" | "csv";
|
|
15
15
|
}
|
package/dist/lib/registry.d.ts
CHANGED
|
@@ -26,17 +26,7 @@ export declare function loadRegistry(cwd?: string): SkillMeta[];
|
|
|
26
26
|
/** Invalidate the registry cache (e.g. after installing a custom skill). */
|
|
27
27
|
export declare function clearRegistryCache(): void;
|
|
28
28
|
export declare function getSkillsByCategory(category: Category): SkillMeta[];
|
|
29
|
-
export
|
|
29
|
+
export { searchSkills, findSimilarSkills } from "./search.js";
|
|
30
30
|
export declare function getSkill(name: string): SkillMeta | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* Return all skills whose tags include a partial case-insensitive match for `tag`.
|
|
33
|
-
*/
|
|
34
31
|
export declare function getSkillsByTag(tag: string): SkillMeta[];
|
|
35
|
-
/**
|
|
36
|
-
* Return all unique tags across every skill, sorted alphabetically.
|
|
37
|
-
*/
|
|
38
32
|
export declare function getAllTags(): string[];
|
|
39
|
-
/**
|
|
40
|
-
* Find skills with names similar to the given query (for "did you mean?" suggestions)
|
|
41
|
-
*/
|
|
42
|
-
export declare function findSimilarSkills(query: string, maxResults?: number): string[];
|
package/dist/lib/scheduler.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface SkillSchedule {
|
|
|
19
19
|
lastRunStatus?: "success" | "error";
|
|
20
20
|
nextRun?: string;
|
|
21
21
|
}
|
|
22
|
-
/** Validate a 5-field cron expression
|
|
22
|
+
/** Validate a 5-field cron expression with range checking. */
|
|
23
23
|
export declare function validateCron(expr: string): {
|
|
24
24
|
valid: boolean;
|
|
25
25
|
error?: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill search — fuzzy matching with edit-distance and prefix scoring
|
|
3
|
+
*/
|
|
4
|
+
import { type SkillMeta } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Compute Levenshtein edit distance between two strings.
|
|
7
|
+
* Uses a 2-row DP approach — no external deps.
|
|
8
|
+
*/
|
|
9
|
+
export declare function editDistance(a: string, b: string): number;
|
|
10
|
+
/**
|
|
11
|
+
* Search skills by name, description, and tags using fuzzy matching.
|
|
12
|
+
*/
|
|
13
|
+
export declare function searchSkills(query: string): SkillMeta[];
|
|
14
|
+
/**
|
|
15
|
+
* Find skills with names similar to the given query (for "did you mean?" suggestions).
|
|
16
|
+
*/
|
|
17
|
+
export declare function findSimilarSkills(query: string, maxResults?: number): string[];
|
package/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-commitpush
|
|
3
|
+
description: Create logical commits from all staged and unstaged changes, then push directly to main. Groups related changes into separate commits with conventional commit messages. Use when you want to commit and push without creating a PR.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Commit & Push to Main
|
|
8
|
+
|
|
9
|
+
Create logical, well-organized commits from the current working directory changes and push directly to `main`.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. Analyze changes: run `git status` and `git diff --stat`.
|
|
14
|
+
2. Group logically:
|
|
15
|
+
- feature code and its tests
|
|
16
|
+
- documentation changes
|
|
17
|
+
- config or dependency changes
|
|
18
|
+
- refactors separate from new behavior
|
|
19
|
+
3. Skip temp or generated files:
|
|
20
|
+
- `.agents-data/`, `.tmp/`, uploaded blobs
|
|
21
|
+
- non-package-manager lockfiles
|
|
22
|
+
- `.claude/scheduled_tasks.lock`
|
|
23
|
+
4. Stage and commit each logical group:
|
|
24
|
+
- use `git add <specific files>`
|
|
25
|
+
- write a conventional commit message (`feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`)
|
|
26
|
+
- explain why in the body when needed
|
|
27
|
+
5. Push to `origin main`
|
|
28
|
+
6. Report the commits created with short hashes and messages
|
|
29
|
+
|
|
30
|
+
## Commit message format
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
type: concise description of the change
|
|
34
|
+
|
|
35
|
+
Optional body explaining why the change was made,
|
|
36
|
+
not what was changed.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use a heredoc when the message needs a body:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git commit -m "$(cat <<'EOF'
|
|
43
|
+
type: description
|
|
44
|
+
|
|
45
|
+
Body if needed.
|
|
46
|
+
EOF
|
|
47
|
+
)"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
- Never use `git add .` or `git add -A`
|
|
53
|
+
- Never commit secrets, `.env` files, or credentials
|
|
54
|
+
- Never amend existing commits unless the user explicitly asks
|
|
55
|
+
- Never force-push
|
|
56
|
+
- If there are no changes, say so and stop
|
|
57
|
+
- Prefer fewer, well-scoped commits over many tiny ones
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasnaxyz/skill-commitpush",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Instruction-set skill for logical commits and direct pushes to main",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"skill-commitpush": "src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"tsconfig.json"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "restricted"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"git",
|
|
20
|
+
"commit",
|
|
21
|
+
"push",
|
|
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-commitpush - Instruction-set skill
|
|
7
|
+
|
|
8
|
+
DESCRIPTION:
|
|
9
|
+
Guides an agent through:
|
|
10
|
+
- scanning repo changes
|
|
11
|
+
- grouping them into logical commits
|
|
12
|
+
- writing conventional commit messages
|
|
13
|
+
- pushing directly to origin/main
|
|
14
|
+
|
|
15
|
+
USAGE:
|
|
16
|
+
skills docs commitpush
|
|
17
|
+
skills install commitpush --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 commitpush --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
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-commitpushpr
|
|
3
|
+
description: Create logical commits from all changes, push to a new branch, and open a pull request on GitHub. Groups related changes into separate commits with conventional commit messages. Use when you want to submit changes for review via PR instead of pushing directly to main.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Commit, Push & Create PR
|
|
8
|
+
|
|
9
|
+
Create logical, well-organized commits from the current working directory changes, push to a feature branch, and open a pull request.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. Analyze changes with `git status` and `git diff --stat`.
|
|
14
|
+
2. Determine a descriptive branch name:
|
|
15
|
+
- `type/short-description`
|
|
16
|
+
- if already on a non-main branch, use it
|
|
17
|
+
3. Create and switch to that branch if you are on `main`
|
|
18
|
+
4. Group related changes into logical commits
|
|
19
|
+
5. Skip temp or generated files:
|
|
20
|
+
- `.agents-data/`, `.tmp/`, uploaded blobs
|
|
21
|
+
- non-package-manager lockfiles
|
|
22
|
+
- `.claude/scheduled_tasks.lock`
|
|
23
|
+
6. Stage specific files and commit with conventional messages
|
|
24
|
+
7. Push with `git push -u origin <branch-name>`
|
|
25
|
+
8. Create a PR with `gh pr create`
|
|
26
|
+
9. Report the commits created and the PR URL
|
|
27
|
+
|
|
28
|
+
## Commit message format
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
type: concise description of the change
|
|
32
|
+
|
|
33
|
+
Optional body explaining why the change was made,
|
|
34
|
+
not what was changed.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## PR body format
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
## Summary
|
|
41
|
+
- bullet points
|
|
42
|
+
|
|
43
|
+
## Test plan
|
|
44
|
+
- [ ] checklist items
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
- Never use `git add .` or `git add -A`
|
|
50
|
+
- Never commit secrets, `.env` files, or credentials
|
|
51
|
+
- Never amend existing commits unless the user explicitly asks
|
|
52
|
+
- Never force-push
|
|
53
|
+
- Never push directly to `main`
|
|
54
|
+
- If there are no changes, say so and stop
|
|
55
|
+
- If already on a feature branch, reuse it
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasnaxyz/skill-commitpushpr",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Instruction-set skill for logical commits, feature-branch pushes, and pull requests",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"skill-commitpushpr": "src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"tsconfig.json"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "restricted"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"git",
|
|
20
|
+
"commit",
|
|
21
|
+
"pull-request",
|
|
22
|
+
"github",
|
|
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
|
+
}
|