unagent 0.0.1 → 0.0.3

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 (38) hide show
  1. package/README.md +47 -0
  2. package/dist/env/{index.d.ts → index.d.mts} +1 -1
  3. package/dist/env/{index.js → index.mjs} +2 -2
  4. package/dist/{env-BUegcU7a.js → env-BcNA2wGd.mjs} +1 -1
  5. package/dist/git/{index.d.ts → index.d.mts} +1 -1
  6. package/dist/git/{index.js → index.mjs} +1 -1
  7. package/dist/git-BczcRFau.mjs +183 -0
  8. package/dist/{index-LzafUiEo.d.ts → index-1wm94iym.d.mts} +1 -1
  9. package/dist/index-BT7sxFbS.d.mts +47 -0
  10. package/dist/{index-DwU61LUW.d.ts → index-C9yyPAid.d.mts} +10 -10
  11. package/dist/{index.d.ts → index.d.mts} +8 -8
  12. package/dist/{index.js → index.mjs} +9 -9
  13. package/dist/link/{index.d.ts → index.d.mts} +1 -1
  14. package/dist/link/{index.js → index.mjs} +1 -1
  15. package/dist/lock/{index.d.ts → index.d.mts} +1 -1
  16. package/dist/lock/{index.js → index.mjs} +1 -1
  17. package/dist/{lock-BeR43Izo.js → lock-B07ZofNa.mjs} +1 -1
  18. package/dist/{path-nPuHl-f5.js → path-D5oePhrf.mjs} +3 -3
  19. package/dist/skill/index.d.mts +2 -0
  20. package/dist/skill/index.mjs +3 -0
  21. package/dist/skill-Bnz84tJ5.mjs +125 -0
  22. package/dist/source/{index.d.ts → index.d.mts} +1 -1
  23. package/dist/source/{index.js → index.mjs} +1 -1
  24. package/dist/utils/{index.d.ts → index.d.mts} +1 -1
  25. package/dist/utils/{index.js → index.mjs} +2 -2
  26. package/package.json +46 -33
  27. package/dist/git-D4ZclaF6.js +0 -132
  28. package/dist/index-y5JZ6STt.d.ts +0 -36
  29. package/dist/skill/index.d.ts +0 -2
  30. package/dist/skill/index.js +0 -3
  31. package/dist/skill-BmLJYiQz.js +0 -69
  32. /package/dist/{index-DmiDvQ8Q.d.ts → index-BMUPEJgp.d.mts} +0 -0
  33. /package/dist/{index-BW91Ai1Y.d.ts → index-Bzc-AUnz.d.mts} +0 -0
  34. /package/dist/{index-CirIJDiq.d.ts → index-CzPUtptc.d.mts} +0 -0
  35. /package/dist/{index-CFwFmDD-.d.ts → index-QWAv-PAT.d.mts} +0 -0
  36. /package/dist/{link-C4PSDr4v.js → link-CnwXkdzo.mjs} +0 -0
  37. /package/dist/{source-IfQxnt_F.js → source-uxXVyVOh.mjs} +0 -0
  38. /package/dist/{utils-CDyPZons.js → utils-BpTrIde9.mjs} +0 -0
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # unagent
2
+
3
+ [![npm version](https://img.shields.io/npm/v/unagent?color=f43f5e)](https://npmjs.com/package/unagent)
4
+ [![npm downloads](https://img.shields.io/npm/dm/unagent?color=f43f5e)](https://npmjs.com/package/unagent)
5
+
6
+ Unified primitives for AI coding agents.
7
+
8
+ - Detect 45+ AI coding agents by env vars or config
9
+ - Discover and parse markdown-based skill files
10
+ - Parse GitHub/GitLab URLs, owner/repo shortcuts, local paths
11
+ - Clone repos, manage temp dirs, check git status
12
+ - Copy directories, create symlinks safely
13
+ - Track installed skills with lockfile and hashes
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install unagent
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { detectCurrentAgent, discoverSkills, parseSource } from 'unagent'
25
+
26
+ // Detect which agent is running
27
+ const agent = detectCurrentAgent()
28
+ if (agent) {
29
+ console.log(`Running in ${agent.config.name}`)
30
+ }
31
+
32
+ // Discover skills
33
+ const skills = discoverSkills('~/.claude/skills', { recursive: true })
34
+
35
+ // Parse source strings
36
+ parseSource('unjs/unagent') // github
37
+ parseSource('github:user/repo#main') // github with ref
38
+ parseSource('./local/path') // local
39
+ ```
40
+
41
+ ## Documentation
42
+
43
+ [unagent.onmax.me](https://unagent.onmax.me)
44
+
45
+ ## License
46
+
47
+ MIT
@@ -1,2 +1,2 @@
1
- import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "../index-BW91Ai1Y.js";
1
+ import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "../index-Bzc-AUnz.mjs";
2
2
  export { AgentConfig, DetectedAgent, XDGPaths, agentConfigExists, agents, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getXDGPaths, hasTTY, isCI, isRunningInAgent, isTTY };
@@ -1,4 +1,4 @@
1
- import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "../env-BUegcU7a.js";
2
- import "../path-nPuHl-f5.js";
1
+ import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "../env-BcNA2wGd.mjs";
2
+ import "../path-D5oePhrf.mjs";
3
3
 
4
4
  export { agentConfigExists, agents, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getXDGPaths, hasTTY, isCI, isRunningInAgent, isTTY };
@@ -1,4 +1,4 @@
1
- import { t as expandPath } from "./path-nPuHl-f5.js";
1
+ import { t as expandPath } from "./path-D5oePhrf.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "pathe";
@@ -1,2 +1,2 @@
1
- import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "../index-DwU61LUW.js";
1
+ import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "../index-C9yyPAid.mjs";
2
2
  export { CloneOptions, CloneResult, GitCloneError, GitStatus, checkout, cleanupTempDir, cloneRepo, cloneToTemp, fetch, getCurrentBranch, getGitStatus, getLatestCommitHash, getRemoteUrl, hasUncommittedChanges, isTempDir, pull };
@@ -1,3 +1,3 @@
1
- import { a as getLatestCommitHash, c as pull, d as cloneRepo, f as cloneToTemp, i as getGitStatus, l as GitCloneError, n as fetch, o as getRemoteUrl, p as isTempDir, r as getCurrentBranch, s as hasUncommittedChanges, t as checkout, u as cleanupTempDir } from "../git-D4ZclaF6.js";
1
+ import { a as getLatestCommitHash, c as pull, d as cloneRepo, f as cloneToTemp, i as getGitStatus, l as GitCloneError, n as fetch, o as getRemoteUrl, p as isTempDir, r as getCurrentBranch, s as hasUncommittedChanges, t as checkout, u as cleanupTempDir } from "../git-BczcRFau.mjs";
2
2
 
3
3
  export { GitCloneError, checkout, cleanupTempDir, cloneRepo, cloneToTemp, fetch, getCurrentBranch, getGitStatus, getLatestCommitHash, getRemoteUrl, hasUncommittedChanges, isTempDir, pull };
@@ -0,0 +1,183 @@
1
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "pathe";
4
+ import { execSync } from "node:child_process";
5
+
6
+ //#region src/git/_exec.ts
7
+ function git(args, opts) {
8
+ return execSync(`git ${args.join(" ")}`, {
9
+ cwd: opts?.cwd,
10
+ timeout: opts?.timeout,
11
+ encoding: "utf8",
12
+ stdio: [
13
+ "pipe",
14
+ "pipe",
15
+ "pipe"
16
+ ]
17
+ }).trim();
18
+ }
19
+ function parseGitStatus(output) {
20
+ const lines = output.split("\n").filter(Boolean);
21
+ const result = {
22
+ branch: "HEAD",
23
+ ahead: 0,
24
+ behind: 0,
25
+ staged: [],
26
+ modified: [],
27
+ untracked: []
28
+ };
29
+ for (const line of lines) if (line.startsWith("# branch.head ")) result.branch = line.slice(14);
30
+ else if (line.startsWith("# branch.ab ")) {
31
+ const match = line.match(/\+(\d+) -(\d+)/);
32
+ if (match) {
33
+ result.ahead = Number.parseInt(match[1], 10);
34
+ result.behind = Number.parseInt(match[2], 10);
35
+ }
36
+ } else if (line.startsWith("1 ") || line.startsWith("2 ")) {
37
+ const xy = line.slice(2, 4);
38
+ const path = line.split(" ").pop() || line.split(" ").pop() || "";
39
+ if (xy[0] !== ".") result.staged.push(path);
40
+ if (xy[1] !== ".") result.modified.push(path);
41
+ } else if (line.startsWith("? ")) result.untracked.push(line.slice(2));
42
+ return result;
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/git/clone.ts
47
+ var GitCloneError = class extends Error {
48
+ url;
49
+ cause;
50
+ constructor(message, url, cause) {
51
+ super(message);
52
+ this.name = "GitCloneError";
53
+ this.url = url;
54
+ this.cause = cause;
55
+ }
56
+ };
57
+ function cloneRepo(url, dest, options = {}) {
58
+ const { depth = 1, branch, timeout = 6e4 } = options;
59
+ try {
60
+ const args = ["clone"];
61
+ if (depth > 0) args.push("--depth", String(depth));
62
+ if (branch) args.push("--branch", branch);
63
+ args.push(url, dest);
64
+ const destDir = join(dest, "..");
65
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
66
+ git(args, { timeout });
67
+ return {
68
+ success: true,
69
+ path: dest
70
+ };
71
+ } catch (error) {
72
+ return {
73
+ success: false,
74
+ path: dest,
75
+ error: error instanceof Error ? error.message : "Unknown error"
76
+ };
77
+ }
78
+ }
79
+ function cloneToTemp(url, options = {}) {
80
+ const { tempDir = tmpdir() } = options;
81
+ return cloneRepo(url, join(tempDir, `unagent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`), options);
82
+ }
83
+ function cleanupTempDir(path) {
84
+ const temp = tmpdir();
85
+ if (!path.startsWith(temp)) return false;
86
+ try {
87
+ if (existsSync(path)) rmSync(path, {
88
+ recursive: true,
89
+ force: true
90
+ });
91
+ return true;
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ function isTempDir(path) {
97
+ return path.startsWith(tmpdir());
98
+ }
99
+
100
+ //#endregion
101
+ //#region src/git/operations.ts
102
+ function getGitStatus(dir) {
103
+ if (!existsSync(dir)) return void 0;
104
+ try {
105
+ git(["rev-parse", "--is-inside-work-tree"], { cwd: dir });
106
+ const parsed = parseGitStatus(git([
107
+ "status",
108
+ "--porcelain=v2",
109
+ "--branch"
110
+ ], { cwd: dir }));
111
+ return {
112
+ isRepo: true,
113
+ branch: parsed.branch,
114
+ ahead: parsed.ahead,
115
+ behind: parsed.behind,
116
+ staged: parsed.staged,
117
+ modified: parsed.modified,
118
+ untracked: parsed.untracked,
119
+ hasChanges: parsed.staged.length > 0 || parsed.modified.length > 0 || parsed.untracked.length > 0
120
+ };
121
+ } catch {
122
+ return;
123
+ }
124
+ }
125
+ function getCurrentBranch(dir) {
126
+ try {
127
+ return git([
128
+ "rev-parse",
129
+ "--abbrev-ref",
130
+ "HEAD"
131
+ ], { cwd: dir });
132
+ } catch {
133
+ return;
134
+ }
135
+ }
136
+ function checkout(dir, ref) {
137
+ try {
138
+ git(["checkout", ref], { cwd: dir });
139
+ return true;
140
+ } catch {
141
+ return false;
142
+ }
143
+ }
144
+ function pull(dir) {
145
+ try {
146
+ git(["pull"], { cwd: dir });
147
+ return true;
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
152
+ function fetch(dir, remote = "origin") {
153
+ try {
154
+ git(["fetch", remote], { cwd: dir });
155
+ return true;
156
+ } catch {
157
+ return false;
158
+ }
159
+ }
160
+ function getRemoteUrl(dir, remote = "origin") {
161
+ try {
162
+ return git([
163
+ "remote",
164
+ "get-url",
165
+ remote
166
+ ], { cwd: dir });
167
+ } catch {
168
+ return;
169
+ }
170
+ }
171
+ function getLatestCommitHash(dir) {
172
+ try {
173
+ return git(["rev-parse", "HEAD"], { cwd: dir });
174
+ } catch {
175
+ return;
176
+ }
177
+ }
178
+ function hasUncommittedChanges(dir) {
179
+ return getGitStatus(dir)?.hasChanges ?? false;
180
+ }
181
+
182
+ //#endregion
183
+ export { getLatestCommitHash as a, pull as c, cloneRepo as d, cloneToTemp as f, getGitStatus as i, GitCloneError as l, fetch as n, getRemoteUrl as o, isTempDir as p, getCurrentBranch as r, hasUncommittedChanges as s, checkout as t, cleanupTempDir as u };
@@ -3,7 +3,7 @@ declare function computeContentHash(content: string): string;
3
3
  declare function computeFileHash(filePath: string): string | undefined;
4
4
  declare function computeDirectoryHash(dirPath: string, exclude?: string[]): string | undefined;
5
5
  //#endregion
6
- //#region src/lock/lock.d.ts
6
+ //#region src/lock/index.d.ts
7
7
  interface SkillLockEntry {
8
8
  name: string;
9
9
  source: string;
@@ -0,0 +1,47 @@
1
+ //#region src/skill/parse.d.ts
2
+ interface SkillFrontmatter {
3
+ "name": string;
4
+ "description": string;
5
+ "license"?: string;
6
+ "compatibility"?: string;
7
+ "metadata"?: Record<string, string>;
8
+ "allowed-tools"?: string;
9
+ "globs"?: string | string[];
10
+ "alwaysApply"?: boolean;
11
+ "tags"?: string[];
12
+ }
13
+ interface ParsedSkill {
14
+ frontmatter: SkillFrontmatter;
15
+ content: string;
16
+ raw: string;
17
+ }
18
+ declare function parseSkillMd(content: string): ParsedSkill;
19
+ declare function extractSkillName(frontmatter: SkillFrontmatter, filename?: string): string;
20
+ /** @deprecated Use validateSkill from './validate' for spec-compliant validation */
21
+ declare function validateSkillMd(parsed: ParsedSkill): string[];
22
+ //#endregion
23
+ //#region src/skill/discover.d.ts
24
+ interface DiscoveredSkill {
25
+ path: string;
26
+ name: string;
27
+ parsed: ParsedSkill;
28
+ }
29
+ interface DiscoverOptions {
30
+ recursive?: boolean;
31
+ }
32
+ declare function discoverSkills(dir: string, options?: DiscoverOptions): DiscoveredSkill[];
33
+ declare function filterSkills(skills: DiscoveredSkill[], query: string): DiscoveredSkill[];
34
+ declare function findSkillByName(skills: DiscoveredSkill[], name: string): DiscoveredSkill | undefined;
35
+ //#endregion
36
+ //#region src/skill/prompt.d.ts
37
+ declare function toPromptXml(skills: DiscoveredSkill[]): string;
38
+ //#endregion
39
+ //#region src/skill/validate.d.ts
40
+ interface ValidationResult {
41
+ valid: boolean;
42
+ errors: string[];
43
+ warnings: string[];
44
+ }
45
+ declare function validateSkill(skill: ParsedSkill, dirName?: string): ValidationResult;
46
+ //#endregion
47
+ export { DiscoveredSkill as a, findSkillByName as c, extractSkillName as d, parseSkillMd as f, DiscoverOptions as i, ParsedSkill as l, validateSkill as n, discoverSkills as o, validateSkillMd as p, toPromptXml as r, filterSkills as s, ValidationResult as t, SkillFrontmatter as u };
@@ -15,8 +15,8 @@ interface CloneResult {
15
15
  path: string;
16
16
  error?: string;
17
17
  }
18
- declare function cloneRepo(url: string, dest: string, options?: CloneOptions): Promise<CloneResult>;
19
- declare function cloneToTemp(url: string, options?: CloneOptions): Promise<CloneResult>;
18
+ declare function cloneRepo(url: string, dest: string, options?: CloneOptions): CloneResult;
19
+ declare function cloneToTemp(url: string, options?: CloneOptions): CloneResult;
20
20
  declare function cleanupTempDir(path: string): boolean;
21
21
  declare function isTempDir(path: string): boolean;
22
22
  //#endregion
@@ -31,13 +31,13 @@ interface GitStatus {
31
31
  untracked: string[];
32
32
  hasChanges: boolean;
33
33
  }
34
- declare function getGitStatus(dir: string): Promise<GitStatus | undefined>;
35
- declare function getCurrentBranch(dir: string): Promise<string | undefined>;
36
- declare function checkout(dir: string, ref: string): Promise<boolean>;
37
- declare function pull(dir: string): Promise<boolean>;
38
- declare function fetch(dir: string, remote?: string): Promise<boolean>;
39
- declare function getRemoteUrl(dir: string, remote?: string): Promise<string | undefined>;
40
- declare function getLatestCommitHash(dir: string): Promise<string | undefined>;
41
- declare function hasUncommittedChanges(dir: string): Promise<boolean>;
34
+ declare function getGitStatus(dir: string): GitStatus | undefined;
35
+ declare function getCurrentBranch(dir: string): string | undefined;
36
+ declare function checkout(dir: string, ref: string): boolean;
37
+ declare function pull(dir: string): boolean;
38
+ declare function fetch(dir: string, remote?: string): boolean;
39
+ declare function getRemoteUrl(dir: string, remote?: string): string | undefined;
40
+ declare function getLatestCommitHash(dir: string): string | undefined;
41
+ declare function hasUncommittedChanges(dir: string): boolean;
42
42
  //#endregion
43
43
  export { getGitStatus as a, hasUncommittedChanges as c, CloneResult as d, GitCloneError as f, isTempDir as g, cloneToTemp as h, getCurrentBranch as i, pull as l, cloneRepo as m, checkout as n, getLatestCommitHash as o, cleanupTempDir as p, fetch as r, getRemoteUrl as s, GitStatus as t, CloneOptions as u };
@@ -1,8 +1,8 @@
1
- import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "./index-BW91Ai1Y.js";
2
- import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "./index-DwU61LUW.js";
3
- import { a as removeSymlink, c as validateSymlinkTarget, d as copyDirectory, f as copyFile, i as isSymlink, l as CopyOptions, n as SymlinkResult, o as isPathSafe, p as getDirectorySize, r as createSymlink, s as sanitizeName, t as SymlinkOptions, u as CopyResult } from "./index-CFwFmDD-.js";
4
- import { a as getSkillFromLock, c as listSkillsInLock, d as writeSkillLock, f as computeContentHash, i as createEmptyLock, l as readSkillLock, m as computeFileHash, n as SkillLockEntry, o as hasSkillInLock, p as computeDirectoryHash, r as addSkillToLock, s as isSkillOutdated, t as SkillLock, u as removeSkillFromLock } from "./index-LzafUiEo.js";
5
- import { a as findSkillByName, c as extractSkillName, i as filterSkills, l as parseSkillMd, n as DiscoveredSkill, o as ParsedSkill, r as discoverSkills, s as SkillFrontmatter, t as DiscoverOptions, u as validateSkillMd } from "./index-y5JZ6STt.js";
6
- import { a as parseOwnerRepo, c as SourceProvider, d as getProviderFromUrl, f as parseGitHubUrl, i as isUrl, l as buildGitHubCloneUrl, n as getOwnerRepo, o as parseSource, p as parseGitLabUrl, r as isLocalPath, s as ProviderInfo, t as ParsedSource, u as buildGitLabCloneUrl } from "./index-DmiDvQ8Q.js";
7
- import { a as truncate, c as bold, d as gray, f as green, h as yellow, i as pluralize, l as cyan, m as stripAnsi, n as shortenPath, o as ANSI, p as red, r as formatList, s as blue, t as expandPath, u as dim } from "./index-CirIJDiq.js";
8
- export { ANSI, AgentConfig, CloneOptions, CloneResult, CopyOptions, CopyResult, DetectedAgent, DiscoverOptions, DiscoveredSkill, GitCloneError, GitStatus, ParsedSkill, ParsedSource, ProviderInfo, SkillFrontmatter, SkillLock, SkillLockEntry, SourceProvider, SymlinkOptions, SymlinkResult, XDGPaths, addSkillToLock, agentConfigExists, agents, blue, bold, buildGitHubCloneUrl, buildGitLabCloneUrl, checkout, cleanupTempDir, cloneRepo, cloneToTemp, computeContentHash, computeDirectoryHash, computeFileHash, copyDirectory, copyFile, createEmptyLock, createSymlink, cyan, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, dim, discoverSkills, expandPath, extractSkillName, fetch, filterSkills, findSkillByName, formatList, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getCurrentBranch, getDirectorySize, getGitStatus, getLatestCommitHash, getOwnerRepo, getProviderFromUrl, getRemoteUrl, getSkillFromLock, getXDGPaths, gray, green, hasSkillInLock, hasTTY, hasUncommittedChanges, isCI, isLocalPath, isPathSafe, isRunningInAgent, isSkillOutdated, isSymlink, isTTY, isTempDir, isUrl, listSkillsInLock, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSkillMd, parseSource, pluralize, pull, readSkillLock, red, removeSkillFromLock, removeSymlink, sanitizeName, shortenPath, stripAnsi, truncate, validateSkillMd, validateSymlinkTarget, writeSkillLock, yellow };
1
+ import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "./index-Bzc-AUnz.mjs";
2
+ import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "./index-C9yyPAid.mjs";
3
+ import { a as removeSymlink, c as validateSymlinkTarget, d as copyDirectory, f as copyFile, i as isSymlink, l as CopyOptions, n as SymlinkResult, o as isPathSafe, p as getDirectorySize, r as createSymlink, s as sanitizeName, t as SymlinkOptions, u as CopyResult } from "./index-QWAv-PAT.mjs";
4
+ import { a as getSkillFromLock, c as listSkillsInLock, d as writeSkillLock, f as computeContentHash, i as createEmptyLock, l as readSkillLock, m as computeFileHash, n as SkillLockEntry, o as hasSkillInLock, p as computeDirectoryHash, r as addSkillToLock, s as isSkillOutdated, t as SkillLock, u as removeSkillFromLock } from "./index-1wm94iym.mjs";
5
+ import { a as DiscoveredSkill, c as findSkillByName, d as extractSkillName, f as parseSkillMd, i as DiscoverOptions, l as ParsedSkill, n as validateSkill, o as discoverSkills, p as validateSkillMd, r as toPromptXml, s as filterSkills, t as ValidationResult, u as SkillFrontmatter } from "./index-BT7sxFbS.mjs";
6
+ import { a as parseOwnerRepo, c as SourceProvider, d as getProviderFromUrl, f as parseGitHubUrl, i as isUrl, l as buildGitHubCloneUrl, n as getOwnerRepo, o as parseSource, p as parseGitLabUrl, r as isLocalPath, s as ProviderInfo, t as ParsedSource, u as buildGitLabCloneUrl } from "./index-BMUPEJgp.mjs";
7
+ import { a as truncate, c as bold, d as gray, f as green, h as yellow, i as pluralize, l as cyan, m as stripAnsi, n as shortenPath, o as ANSI, p as red, r as formatList, s as blue, t as expandPath, u as dim } from "./index-CzPUtptc.mjs";
8
+ export { ANSI, AgentConfig, CloneOptions, CloneResult, CopyOptions, CopyResult, DetectedAgent, DiscoverOptions, DiscoveredSkill, GitCloneError, GitStatus, ParsedSkill, ParsedSource, ProviderInfo, SkillFrontmatter, SkillLock, SkillLockEntry, SourceProvider, SymlinkOptions, SymlinkResult, ValidationResult, XDGPaths, addSkillToLock, agentConfigExists, agents, blue, bold, buildGitHubCloneUrl, buildGitLabCloneUrl, checkout, cleanupTempDir, cloneRepo, cloneToTemp, computeContentHash, computeDirectoryHash, computeFileHash, copyDirectory, copyFile, createEmptyLock, createSymlink, cyan, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, dim, discoverSkills, expandPath, extractSkillName, fetch, filterSkills, findSkillByName, formatList, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getCurrentBranch, getDirectorySize, getGitStatus, getLatestCommitHash, getOwnerRepo, getProviderFromUrl, getRemoteUrl, getSkillFromLock, getXDGPaths, gray, green, hasSkillInLock, hasTTY, hasUncommittedChanges, isCI, isLocalPath, isPathSafe, isRunningInAgent, isSkillOutdated, isSymlink, isTTY, isTempDir, isUrl, listSkillsInLock, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSkillMd, parseSource, pluralize, pull, readSkillLock, red, removeSkillFromLock, removeSymlink, sanitizeName, shortenPath, stripAnsi, toPromptXml, truncate, validateSkill, validateSkillMd, validateSymlinkTarget, writeSkillLock, yellow };
@@ -1,10 +1,10 @@
1
- import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "./env-BUegcU7a.js";
2
- import { n as shortenPath, t as expandPath } from "./path-nPuHl-f5.js";
3
- import { a as getLatestCommitHash, c as pull, d as cloneRepo, f as cloneToTemp, i as getGitStatus, l as GitCloneError, n as fetch, o as getRemoteUrl, p as isTempDir, r as getCurrentBranch, s as hasUncommittedChanges, t as checkout, u as cleanupTempDir } from "./git-D4ZclaF6.js";
4
- import { a as sanitizeName, c as copyFile, i as isPathSafe, l as getDirectorySize, n as isSymlink, o as validateSymlinkTarget, r as removeSymlink, s as copyDirectory, t as createSymlink } from "./link-C4PSDr4v.js";
5
- import { a as isSkillOutdated, c as removeSkillFromLock, d as computeDirectoryHash, f as computeFileHash, i as hasSkillInLock, l as writeSkillLock, n as createEmptyLock, o as listSkillsInLock, r as getSkillFromLock, s as readSkillLock, t as addSkillToLock, u as computeContentHash } from "./lock-BeR43Izo.js";
6
- import { a as parseSkillMd, i as extractSkillName, n as filterSkills, o as validateSkillMd, r as findSkillByName, t as discoverSkills } from "./skill-BmLJYiQz.js";
7
- import { a as parseSource, c as getProviderFromUrl, i as parseOwnerRepo, l as parseGitHubUrl, n as isLocalPath, o as buildGitHubCloneUrl, r as isUrl, s as buildGitLabCloneUrl, t as getOwnerRepo, u as parseGitLabUrl } from "./source-IfQxnt_F.js";
8
- import { a as blue, c as dim, d as red, f as stripAnsi, i as ANSI, l as gray, n as pluralize, o as bold, p as yellow, r as truncate, s as cyan, t as formatList, u as green } from "./utils-CDyPZons.js";
1
+ import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "./env-BcNA2wGd.mjs";
2
+ import { n as shortenPath, t as expandPath } from "./path-D5oePhrf.mjs";
3
+ import { a as getLatestCommitHash, c as pull, d as cloneRepo, f as cloneToTemp, i as getGitStatus, l as GitCloneError, n as fetch, o as getRemoteUrl, p as isTempDir, r as getCurrentBranch, s as hasUncommittedChanges, t as checkout, u as cleanupTempDir } from "./git-BczcRFau.mjs";
4
+ import { a as sanitizeName, c as copyFile, i as isPathSafe, l as getDirectorySize, n as isSymlink, o as validateSymlinkTarget, r as removeSymlink, s as copyDirectory, t as createSymlink } from "./link-CnwXkdzo.mjs";
5
+ import { a as isSkillOutdated, c as removeSkillFromLock, d as computeDirectoryHash, f as computeFileHash, i as hasSkillInLock, l as writeSkillLock, n as createEmptyLock, o as listSkillsInLock, r as getSkillFromLock, s as readSkillLock, t as addSkillToLock, u as computeContentHash } from "./lock-B07ZofNa.mjs";
6
+ import { a as findSkillByName, c as validateSkillMd, i as filterSkills, n as toPromptXml, o as extractSkillName, r as discoverSkills, s as parseSkillMd, t as validateSkill } from "./skill-Bnz84tJ5.mjs";
7
+ import { a as parseSource, c as getProviderFromUrl, i as parseOwnerRepo, l as parseGitHubUrl, n as isLocalPath, o as buildGitHubCloneUrl, r as isUrl, s as buildGitLabCloneUrl, t as getOwnerRepo, u as parseGitLabUrl } from "./source-uxXVyVOh.mjs";
8
+ import { a as blue, c as dim, d as red, f as stripAnsi, i as ANSI, l as gray, n as pluralize, o as bold, p as yellow, r as truncate, s as cyan, t as formatList, u as green } from "./utils-BpTrIde9.mjs";
9
9
 
10
- export { ANSI, GitCloneError, addSkillToLock, agentConfigExists, agents, blue, bold, buildGitHubCloneUrl, buildGitLabCloneUrl, checkout, cleanupTempDir, cloneRepo, cloneToTemp, computeContentHash, computeDirectoryHash, computeFileHash, copyDirectory, copyFile, createEmptyLock, createSymlink, cyan, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, dim, discoverSkills, expandPath, extractSkillName, fetch, filterSkills, findSkillByName, formatList, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getCurrentBranch, getDirectorySize, getGitStatus, getLatestCommitHash, getOwnerRepo, getProviderFromUrl, getRemoteUrl, getSkillFromLock, getXDGPaths, gray, green, hasSkillInLock, hasTTY, hasUncommittedChanges, isCI, isLocalPath, isPathSafe, isRunningInAgent, isSkillOutdated, isSymlink, isTTY, isTempDir, isUrl, listSkillsInLock, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSkillMd, parseSource, pluralize, pull, readSkillLock, red, removeSkillFromLock, removeSymlink, sanitizeName, shortenPath, stripAnsi, truncate, validateSkillMd, validateSymlinkTarget, writeSkillLock, yellow };
10
+ export { ANSI, GitCloneError, addSkillToLock, agentConfigExists, agents, blue, bold, buildGitHubCloneUrl, buildGitLabCloneUrl, checkout, cleanupTempDir, cloneRepo, cloneToTemp, computeContentHash, computeDirectoryHash, computeFileHash, copyDirectory, copyFile, createEmptyLock, createSymlink, cyan, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, dim, discoverSkills, expandPath, extractSkillName, fetch, filterSkills, findSkillByName, formatList, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getCurrentBranch, getDirectorySize, getGitStatus, getLatestCommitHash, getOwnerRepo, getProviderFromUrl, getRemoteUrl, getSkillFromLock, getXDGPaths, gray, green, hasSkillInLock, hasTTY, hasUncommittedChanges, isCI, isLocalPath, isPathSafe, isRunningInAgent, isSkillOutdated, isSymlink, isTTY, isTempDir, isUrl, listSkillsInLock, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSkillMd, parseSource, pluralize, pull, readSkillLock, red, removeSkillFromLock, removeSymlink, sanitizeName, shortenPath, stripAnsi, toPromptXml, truncate, validateSkill, validateSkillMd, validateSymlinkTarget, writeSkillLock, yellow };
@@ -1,2 +1,2 @@
1
- import { a as removeSymlink, c as validateSymlinkTarget, d as copyDirectory, f as copyFile, i as isSymlink, l as CopyOptions, n as SymlinkResult, o as isPathSafe, p as getDirectorySize, r as createSymlink, s as sanitizeName, t as SymlinkOptions, u as CopyResult } from "../index-CFwFmDD-.js";
1
+ import { a as removeSymlink, c as validateSymlinkTarget, d as copyDirectory, f as copyFile, i as isSymlink, l as CopyOptions, n as SymlinkResult, o as isPathSafe, p as getDirectorySize, r as createSymlink, s as sanitizeName, t as SymlinkOptions, u as CopyResult } from "../index-QWAv-PAT.mjs";
2
2
  export { CopyOptions, CopyResult, SymlinkOptions, SymlinkResult, copyDirectory, copyFile, createSymlink, getDirectorySize, isPathSafe, isSymlink, removeSymlink, sanitizeName, validateSymlinkTarget };
@@ -1,3 +1,3 @@
1
- import { a as sanitizeName, c as copyFile, i as isPathSafe, l as getDirectorySize, n as isSymlink, o as validateSymlinkTarget, r as removeSymlink, s as copyDirectory, t as createSymlink } from "../link-C4PSDr4v.js";
1
+ import { a as sanitizeName, c as copyFile, i as isPathSafe, l as getDirectorySize, n as isSymlink, o as validateSymlinkTarget, r as removeSymlink, s as copyDirectory, t as createSymlink } from "../link-CnwXkdzo.mjs";
2
2
 
3
3
  export { copyDirectory, copyFile, createSymlink, getDirectorySize, isPathSafe, isSymlink, removeSymlink, sanitizeName, validateSymlinkTarget };
@@ -1,2 +1,2 @@
1
- import { a as getSkillFromLock, c as listSkillsInLock, d as writeSkillLock, f as computeContentHash, i as createEmptyLock, l as readSkillLock, m as computeFileHash, n as SkillLockEntry, o as hasSkillInLock, p as computeDirectoryHash, r as addSkillToLock, s as isSkillOutdated, t as SkillLock, u as removeSkillFromLock } from "../index-LzafUiEo.js";
1
+ import { a as getSkillFromLock, c as listSkillsInLock, d as writeSkillLock, f as computeContentHash, i as createEmptyLock, l as readSkillLock, m as computeFileHash, n as SkillLockEntry, o as hasSkillInLock, p as computeDirectoryHash, r as addSkillToLock, s as isSkillOutdated, t as SkillLock, u as removeSkillFromLock } from "../index-1wm94iym.mjs";
2
2
  export { SkillLock, SkillLockEntry, addSkillToLock, computeContentHash, computeDirectoryHash, computeFileHash, createEmptyLock, getSkillFromLock, hasSkillInLock, isSkillOutdated, listSkillsInLock, readSkillLock, removeSkillFromLock, writeSkillLock };
@@ -1,3 +1,3 @@
1
- import { a as isSkillOutdated, c as removeSkillFromLock, d as computeDirectoryHash, f as computeFileHash, i as hasSkillInLock, l as writeSkillLock, n as createEmptyLock, o as listSkillsInLock, r as getSkillFromLock, s as readSkillLock, t as addSkillToLock, u as computeContentHash } from "../lock-BeR43Izo.js";
1
+ import { a as isSkillOutdated, c as removeSkillFromLock, d as computeDirectoryHash, f as computeFileHash, i as hasSkillInLock, l as writeSkillLock, n as createEmptyLock, o as listSkillsInLock, r as getSkillFromLock, s as readSkillLock, t as addSkillToLock, u as computeContentHash } from "../lock-B07ZofNa.mjs";
2
2
 
3
3
  export { addSkillToLock, computeContentHash, computeDirectoryHash, computeFileHash, createEmptyLock, getSkillFromLock, hasSkillInLock, isSkillOutdated, listSkillsInLock, readSkillLock, removeSkillFromLock, writeSkillLock };
@@ -35,7 +35,7 @@ function hashDirectory(dir, hash, exclude) {
35
35
  }
36
36
 
37
37
  //#endregion
38
- //#region src/lock/lock.ts
38
+ //#region src/lock/index.ts
39
39
  const LOCK_VERSION = 1;
40
40
  const DEFAULT_LOCK_FILE = "skill.lock";
41
41
  function createEmptyLock() {
@@ -1,9 +1,9 @@
1
1
  import { homedir } from "node:os";
2
- import { relative, resolve } from "pathe";
2
+ import { normalize, relative, resolve } from "pathe";
3
3
 
4
4
  //#region src/utils/path.ts
5
5
  function shortenPath(filepath, cwd) {
6
- const home = homedir();
6
+ const home = normalize(homedir());
7
7
  const resolved = resolve(filepath);
8
8
  if (cwd) {
9
9
  const rel = relative(cwd, resolved);
@@ -13,7 +13,7 @@ function shortenPath(filepath, cwd) {
13
13
  return resolved;
14
14
  }
15
15
  function expandPath(filepath) {
16
- if (filepath.startsWith("~")) return `${homedir()}${filepath.slice(1)}`;
16
+ if (filepath.startsWith("~")) return `${normalize(homedir())}${filepath.slice(1)}`;
17
17
  return resolve(filepath);
18
18
  }
19
19
 
@@ -0,0 +1,2 @@
1
+ import { a as DiscoveredSkill, c as findSkillByName, d as extractSkillName, f as parseSkillMd, i as DiscoverOptions, l as ParsedSkill, n as validateSkill, o as discoverSkills, p as validateSkillMd, r as toPromptXml, s as filterSkills, t as ValidationResult, u as SkillFrontmatter } from "../index-BT7sxFbS.mjs";
2
+ export { DiscoverOptions, DiscoveredSkill, ParsedSkill, SkillFrontmatter, ValidationResult, discoverSkills, extractSkillName, filterSkills, findSkillByName, parseSkillMd, toPromptXml, validateSkill, validateSkillMd };
@@ -0,0 +1,3 @@
1
+ import { a as findSkillByName, c as validateSkillMd, i as filterSkills, n as toPromptXml, o as extractSkillName, r as discoverSkills, s as parseSkillMd, t as validateSkill } from "../skill-Bnz84tJ5.mjs";
2
+
3
+ export { discoverSkills, extractSkillName, filterSkills, findSkillByName, parseSkillMd, toPromptXml, validateSkill, validateSkillMd };
@@ -0,0 +1,125 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { basename, join } from "pathe";
3
+ import { parse } from "yaml";
4
+
5
+ //#region src/skill/_frontmatter.ts
6
+ function parseFrontmatter(input) {
7
+ const match = input.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
8
+ if (!match) return {
9
+ data: {},
10
+ content: input.trim()
11
+ };
12
+ return {
13
+ data: parse(match[1]),
14
+ content: match[2].trim()
15
+ };
16
+ }
17
+
18
+ //#endregion
19
+ //#region src/skill/parse.ts
20
+ function parseSkillMd(content) {
21
+ const { data, content: body } = parseFrontmatter(content);
22
+ return {
23
+ frontmatter: data,
24
+ content: body,
25
+ raw: content
26
+ };
27
+ }
28
+ function extractSkillName(frontmatter, filename) {
29
+ if (frontmatter.name) return frontmatter.name;
30
+ if (filename) return filename.replace(/\.md$/i, "").replace(/^SKILL[._-]?/i, "").replace(/[._-]/g, " ").trim();
31
+ return "Unnamed Skill";
32
+ }
33
+ /** @deprecated Use validateSkill from './validate' for spec-compliant validation */
34
+ function validateSkillMd(parsed) {
35
+ const errors = [];
36
+ if (!parsed.content) errors.push("Skill content is empty");
37
+ if (parsed.frontmatter.globs) {
38
+ const globs = Array.isArray(parsed.frontmatter.globs) ? parsed.frontmatter.globs : [parsed.frontmatter.globs];
39
+ for (const glob of globs) if (typeof glob !== "string") errors.push(`Invalid glob pattern: ${glob}`);
40
+ }
41
+ return errors;
42
+ }
43
+
44
+ //#endregion
45
+ //#region src/skill/discover.ts
46
+ const SKILL_FILE = "SKILL.md";
47
+ function discoverSkills(dir, options = {}) {
48
+ const { recursive = false } = options;
49
+ if (!existsSync(dir)) return [];
50
+ const skills = [];
51
+ const entries = readdirSync(dir);
52
+ for (const entry of entries) {
53
+ const fullPath = join(dir, entry);
54
+ if (!statSync(fullPath).isDirectory()) continue;
55
+ const skillFile = join(fullPath, SKILL_FILE);
56
+ if (existsSync(skillFile)) try {
57
+ const parsed = parseSkillMd(readFileSync(skillFile, "utf-8"));
58
+ skills.push({
59
+ path: fullPath,
60
+ name: basename(fullPath),
61
+ parsed
62
+ });
63
+ } catch {}
64
+ else if (recursive) skills.push(...discoverSkills(fullPath, options));
65
+ }
66
+ return skills;
67
+ }
68
+ function filterSkills(skills, query) {
69
+ const lowerQuery = query.toLowerCase();
70
+ return skills.filter((skill) => {
71
+ const nameMatch = skill.name.toLowerCase().includes(lowerQuery);
72
+ const descMatch = skill.parsed.frontmatter.description?.toLowerCase().includes(lowerQuery);
73
+ const tagMatch = skill.parsed.frontmatter.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery));
74
+ return nameMatch || descMatch || tagMatch;
75
+ });
76
+ }
77
+ function findSkillByName(skills, name) {
78
+ const lowerName = name.toLowerCase();
79
+ return skills.find((skill) => skill.name.toLowerCase() === lowerName);
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/skill/prompt.ts
84
+ function toPromptXml(skills) {
85
+ if (skills.length === 0) return "";
86
+ return `<available_skills>\n${skills.map((skill) => {
87
+ return ` <skill>
88
+ <name>${escapeXml(skill.name)}</name>
89
+ <description>${escapeXml(skill.parsed.frontmatter.description || "")}</description>
90
+ <location>${escapeXml(skill.path)}</location>
91
+ </skill>`;
92
+ }).join("\n")}\n</available_skills>`;
93
+ }
94
+ function escapeXml(str) {
95
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
96
+ }
97
+
98
+ //#endregion
99
+ //#region src/skill/validate.ts
100
+ const NAME_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$/;
101
+ function validateSkill(skill, dirName) {
102
+ const errors = [];
103
+ const warnings = [];
104
+ const { frontmatter, content } = skill;
105
+ if (!frontmatter.name) errors.push("name is required");
106
+ else {
107
+ const name = frontmatter.name;
108
+ if (name.length < 1 || name.length > 64) errors.push("name must be 1-64 characters");
109
+ if (!NAME_PATTERN.test(name)) errors.push("name must be lowercase letters, numbers, and hyphens; cannot start/end with hyphen");
110
+ if (name.includes("--")) errors.push("name cannot contain consecutive hyphens");
111
+ if (dirName && name !== dirName) errors.push(`name "${name}" must match directory name "${dirName}"`);
112
+ }
113
+ if (!frontmatter.description) errors.push("description is required");
114
+ else if (frontmatter.description.length < 1 || frontmatter.description.length > 1024) errors.push("description must be 1-1024 characters");
115
+ if (frontmatter.compatibility && frontmatter.compatibility.length > 500) errors.push("compatibility must be max 500 characters");
116
+ if (!content) warnings.push("skill content is empty");
117
+ return {
118
+ valid: errors.length === 0,
119
+ errors,
120
+ warnings
121
+ };
122
+ }
123
+
124
+ //#endregion
125
+ export { findSkillByName as a, validateSkillMd as c, filterSkills as i, toPromptXml as n, extractSkillName as o, discoverSkills as r, parseSkillMd as s, validateSkill as t };
@@ -1,2 +1,2 @@
1
- import { a as parseOwnerRepo, c as SourceProvider, d as getProviderFromUrl, f as parseGitHubUrl, i as isUrl, l as buildGitHubCloneUrl, n as getOwnerRepo, o as parseSource, p as parseGitLabUrl, r as isLocalPath, s as ProviderInfo, t as ParsedSource, u as buildGitLabCloneUrl } from "../index-DmiDvQ8Q.js";
1
+ import { a as parseOwnerRepo, c as SourceProvider, d as getProviderFromUrl, f as parseGitHubUrl, i as isUrl, l as buildGitHubCloneUrl, n as getOwnerRepo, o as parseSource, p as parseGitLabUrl, r as isLocalPath, s as ProviderInfo, t as ParsedSource, u as buildGitLabCloneUrl } from "../index-BMUPEJgp.mjs";
2
2
  export { ParsedSource, ProviderInfo, SourceProvider, buildGitHubCloneUrl, buildGitLabCloneUrl, getOwnerRepo, getProviderFromUrl, isLocalPath, isUrl, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSource };
@@ -1,3 +1,3 @@
1
- import { a as parseSource, c as getProviderFromUrl, i as parseOwnerRepo, l as parseGitHubUrl, n as isLocalPath, o as buildGitHubCloneUrl, r as isUrl, s as buildGitLabCloneUrl, t as getOwnerRepo, u as parseGitLabUrl } from "../source-IfQxnt_F.js";
1
+ import { a as parseSource, c as getProviderFromUrl, i as parseOwnerRepo, l as parseGitHubUrl, n as isLocalPath, o as buildGitHubCloneUrl, r as isUrl, s as buildGitLabCloneUrl, t as getOwnerRepo, u as parseGitLabUrl } from "../source-uxXVyVOh.mjs";
2
2
 
3
3
  export { buildGitHubCloneUrl, buildGitLabCloneUrl, getOwnerRepo, getProviderFromUrl, isLocalPath, isUrl, parseGitHubUrl, parseGitLabUrl, parseOwnerRepo, parseSource };
@@ -1,2 +1,2 @@
1
- import { a as truncate, c as bold, d as gray, f as green, h as yellow, i as pluralize, l as cyan, m as stripAnsi, n as shortenPath, o as ANSI, p as red, r as formatList, s as blue, t as expandPath, u as dim } from "../index-CirIJDiq.js";
1
+ import { a as truncate, c as bold, d as gray, f as green, h as yellow, i as pluralize, l as cyan, m as stripAnsi, n as shortenPath, o as ANSI, p as red, r as formatList, s as blue, t as expandPath, u as dim } from "../index-CzPUtptc.mjs";
2
2
  export { ANSI, blue, bold, cyan, dim, expandPath, formatList, gray, green, pluralize, red, shortenPath, stripAnsi, truncate, yellow };
@@ -1,4 +1,4 @@
1
- import { n as shortenPath, t as expandPath } from "../path-nPuHl-f5.js";
2
- import { a as blue, c as dim, d as red, f as stripAnsi, i as ANSI, l as gray, n as pluralize, o as bold, p as yellow, r as truncate, s as cyan, t as formatList, u as green } from "../utils-CDyPZons.js";
1
+ import { n as shortenPath, t as expandPath } from "../path-D5oePhrf.mjs";
2
+ import { a as blue, c as dim, d as red, f as stripAnsi, i as ANSI, l as gray, n as pluralize, o as bold, p as yellow, r as truncate, s as cyan, t as formatList, u as green } from "../utils-BpTrIde9.mjs";
3
3
 
4
4
  export { ANSI, blue, bold, cyan, dim, expandPath, formatList, gray, green, pluralize, red, shortenPath, stripAnsi, truncate, yellow };
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "unagent",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.3",
5
5
  "description": "Unified agent primitives for AI coding agents",
6
- "author": "",
6
+ "author": "Maximilian Conradt <https://github.com/onmax>",
7
7
  "license": "MIT",
8
+ "funding": "https://github.com/sponsors/onmax",
8
9
  "homepage": "https://unagent.onmax.me",
9
- "repository": "gh:onmax/unagent",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/onmax/unagent"
13
+ },
10
14
  "keywords": [
11
15
  "unjs",
12
16
  "agent",
@@ -19,64 +23,73 @@
19
23
  "sideEffects": false,
20
24
  "exports": {
21
25
  ".": {
22
- "types": "./dist/index.d.ts",
23
- "import": "./dist/index.js"
26
+ "types": "./dist/index.d.mts",
27
+ "default": "./dist/index.mjs"
24
28
  },
25
29
  "./env": {
26
- "types": "./dist/env/index.d.ts",
27
- "import": "./dist/env/index.js"
30
+ "types": "./dist/env/index.d.mts",
31
+ "default": "./dist/env/index.mjs"
28
32
  },
29
33
  "./skill": {
30
- "types": "./dist/skill/index.d.ts",
31
- "import": "./dist/skill/index.js"
34
+ "types": "./dist/skill/index.d.mts",
35
+ "default": "./dist/skill/index.mjs"
32
36
  },
33
37
  "./link": {
34
- "types": "./dist/link/index.d.ts",
35
- "import": "./dist/link/index.js"
38
+ "types": "./dist/link/index.d.mts",
39
+ "default": "./dist/link/index.mjs"
36
40
  },
37
41
  "./lock": {
38
- "types": "./dist/lock/index.d.ts",
39
- "import": "./dist/lock/index.js"
42
+ "types": "./dist/lock/index.d.mts",
43
+ "default": "./dist/lock/index.mjs"
40
44
  },
41
45
  "./source": {
42
- "types": "./dist/source/index.d.ts",
43
- "import": "./dist/source/index.js"
46
+ "types": "./dist/source/index.d.mts",
47
+ "default": "./dist/source/index.mjs"
44
48
  },
45
49
  "./git": {
46
- "types": "./dist/git/index.d.ts",
47
- "import": "./dist/git/index.js"
50
+ "types": "./dist/git/index.d.mts",
51
+ "default": "./dist/git/index.mjs"
48
52
  },
49
53
  "./utils": {
50
- "types": "./dist/utils/index.d.ts",
51
- "import": "./dist/utils/index.js"
54
+ "types": "./dist/utils/index.d.mts",
55
+ "default": "./dist/utils/index.mjs"
52
56
  }
53
57
  },
58
+ "main": "./dist/index.mjs",
59
+ "module": "./dist/index.mjs",
60
+ "types": "./dist/index.d.mts",
54
61
  "files": [
55
62
  "dist"
56
63
  ],
57
- "scripts": {
58
- "build": "tsdown",
59
- "dev": "tsdown --watch",
60
- "lint": "eslint .",
61
- "lint:fix": "eslint . --fix",
62
- "typecheck": "tsc --noEmit",
63
- "test": "vitest",
64
- "release": "bumpp",
65
- "prepublishOnly": "pnpm build"
66
- },
67
64
  "dependencies": {
68
- "gray-matter": "^4.0.0",
69
65
  "pathe": "^2.0.3",
70
- "simple-git": "^3.0.0",
71
- "std-env": "^3.10.0"
66
+ "std-env": "^3.10.0",
67
+ "yaml": "^2.7.0"
72
68
  },
73
69
  "devDependencies": {
74
70
  "@antfu/eslint-config": "^4.0.0",
75
71
  "@types/node": "^25.1.0",
76
72
  "bumpp": "^9.0.0",
77
73
  "eslint": "^9.0.0",
74
+ "lint-staged": "^16.2.7",
75
+ "simple-git-hooks": "^2.13.1",
78
76
  "tsdown": "^0.15.0",
79
77
  "typescript": "^5.7.0",
80
78
  "vitest": "^3.0.0"
79
+ },
80
+ "simple-git-hooks": {
81
+ "pre-commit": "pnpm lint-staged"
82
+ },
83
+ "lint-staged": {
84
+ "*": "eslint --fix"
85
+ },
86
+ "scripts": {
87
+ "build": "tsdown",
88
+ "dev": "tsdown --watch",
89
+ "lint": "eslint .",
90
+ "lint:fix": "eslint . --fix",
91
+ "typecheck": "tsc --noEmit",
92
+ "test": "vitest",
93
+ "release": "bumpp"
81
94
  }
82
- }
95
+ }
@@ -1,132 +0,0 @@
1
- import { existsSync, mkdirSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "pathe";
4
- import { simpleGit } from "simple-git";
5
-
6
- //#region src/git/clone.ts
7
- var GitCloneError = class extends Error {
8
- url;
9
- cause;
10
- constructor(message, url, cause) {
11
- super(message);
12
- this.name = "GitCloneError";
13
- this.url = url;
14
- this.cause = cause;
15
- }
16
- };
17
- async function cloneRepo(url, dest, options = {}) {
18
- const { depth = 1, branch, timeout = 6e4 } = options;
19
- try {
20
- const git = simpleGit({ timeout: { block: timeout } });
21
- const cloneOptions = [];
22
- if (depth > 0) cloneOptions.push("--depth", String(depth));
23
- if (branch) cloneOptions.push("--branch", branch);
24
- const destDir = join(dest, "..");
25
- if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
26
- await git.clone(url, dest, cloneOptions);
27
- return {
28
- success: true,
29
- path: dest
30
- };
31
- } catch (error) {
32
- return {
33
- success: false,
34
- path: dest,
35
- error: error instanceof Error ? error.message : "Unknown error"
36
- };
37
- }
38
- }
39
- async function cloneToTemp(url, options = {}) {
40
- const { tempDir = tmpdir() } = options;
41
- return cloneRepo(url, join(tempDir, `unagent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`), options);
42
- }
43
- function cleanupTempDir(path) {
44
- const temp = tmpdir();
45
- if (!path.startsWith(temp)) return false;
46
- try {
47
- if (existsSync(path)) rmSync(path, {
48
- recursive: true,
49
- force: true
50
- });
51
- return true;
52
- } catch {
53
- return false;
54
- }
55
- }
56
- function isTempDir(path) {
57
- return path.startsWith(tmpdir());
58
- }
59
-
60
- //#endregion
61
- //#region src/git/operations.ts
62
- async function getGitStatus(dir) {
63
- if (!existsSync(dir)) return void 0;
64
- try {
65
- const git = simpleGit(dir);
66
- if (!await git.checkIsRepo()) return void 0;
67
- const status = await git.status();
68
- return {
69
- isRepo: true,
70
- branch: status.current || "HEAD",
71
- ahead: status.ahead,
72
- behind: status.behind,
73
- staged: status.staged,
74
- modified: status.modified,
75
- untracked: status.not_added,
76
- hasChanges: !status.isClean()
77
- };
78
- } catch {
79
- return;
80
- }
81
- }
82
- async function getCurrentBranch(dir) {
83
- try {
84
- return (await simpleGit(dir).revparse(["--abbrev-ref", "HEAD"])).trim();
85
- } catch {
86
- return;
87
- }
88
- }
89
- async function checkout(dir, ref) {
90
- try {
91
- await simpleGit(dir).checkout(ref);
92
- return true;
93
- } catch {
94
- return false;
95
- }
96
- }
97
- async function pull(dir) {
98
- try {
99
- await simpleGit(dir).pull();
100
- return true;
101
- } catch {
102
- return false;
103
- }
104
- }
105
- async function fetch(dir, remote = "origin") {
106
- try {
107
- await simpleGit(dir).fetch(remote);
108
- return true;
109
- } catch {
110
- return false;
111
- }
112
- }
113
- async function getRemoteUrl(dir, remote = "origin") {
114
- try {
115
- return (await simpleGit(dir).getRemotes(true)).find((r) => r.name === remote)?.refs.fetch;
116
- } catch {
117
- return;
118
- }
119
- }
120
- async function getLatestCommitHash(dir) {
121
- try {
122
- return (await simpleGit(dir).log({ maxCount: 1 })).latest?.hash;
123
- } catch {
124
- return;
125
- }
126
- }
127
- async function hasUncommittedChanges(dir) {
128
- return (await getGitStatus(dir))?.hasChanges ?? false;
129
- }
130
-
131
- //#endregion
132
- export { getLatestCommitHash as a, pull as c, cloneRepo as d, cloneToTemp as f, getGitStatus as i, GitCloneError as l, fetch as n, getRemoteUrl as o, isTempDir as p, getCurrentBranch as r, hasUncommittedChanges as s, checkout as t, cleanupTempDir as u };
@@ -1,36 +0,0 @@
1
- //#region src/skill/parse.d.ts
2
- interface SkillFrontmatter {
3
- name?: string;
4
- description?: string;
5
- version?: string;
6
- author?: string;
7
- globs?: string | string[];
8
- alwaysApply?: boolean;
9
- tags?: string[];
10
- [key: string]: unknown;
11
- }
12
- interface ParsedSkill {
13
- frontmatter: SkillFrontmatter;
14
- content: string;
15
- raw: string;
16
- }
17
- declare function parseSkillMd(content: string): ParsedSkill;
18
- declare function extractSkillName(frontmatter: SkillFrontmatter, filename?: string): string;
19
- declare function validateSkillMd(parsed: ParsedSkill): string[];
20
- //#endregion
21
- //#region src/skill/discover.d.ts
22
- interface DiscoveredSkill {
23
- path: string;
24
- filename: string;
25
- name: string;
26
- parsed: ParsedSkill;
27
- }
28
- interface DiscoverOptions {
29
- recursive?: boolean;
30
- extensions?: string[];
31
- }
32
- declare function discoverSkills(dir: string, options?: DiscoverOptions): DiscoveredSkill[];
33
- declare function filterSkills(skills: DiscoveredSkill[], query: string): DiscoveredSkill[];
34
- declare function findSkillByName(skills: DiscoveredSkill[], name: string): DiscoveredSkill | undefined;
35
- //#endregion
36
- export { findSkillByName as a, extractSkillName as c, filterSkills as i, parseSkillMd as l, DiscoveredSkill as n, ParsedSkill as o, discoverSkills as r, SkillFrontmatter as s, DiscoverOptions as t, validateSkillMd as u };
@@ -1,2 +0,0 @@
1
- import { a as findSkillByName, c as extractSkillName, i as filterSkills, l as parseSkillMd, n as DiscoveredSkill, o as ParsedSkill, r as discoverSkills, s as SkillFrontmatter, t as DiscoverOptions, u as validateSkillMd } from "../index-y5JZ6STt.js";
2
- export { DiscoverOptions, DiscoveredSkill, ParsedSkill, SkillFrontmatter, discoverSkills, extractSkillName, filterSkills, findSkillByName, parseSkillMd, validateSkillMd };
@@ -1,3 +0,0 @@
1
- import { a as parseSkillMd, i as extractSkillName, n as filterSkills, o as validateSkillMd, r as findSkillByName, t as discoverSkills } from "../skill-BmLJYiQz.js";
2
-
3
- export { discoverSkills, extractSkillName, filterSkills, findSkillByName, parseSkillMd, validateSkillMd };
@@ -1,69 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
- import { basename, extname, join } from "pathe";
3
- import matter from "gray-matter";
4
-
5
- //#region src/skill/parse.ts
6
- function parseSkillMd(content) {
7
- const { data, content: body } = matter(content);
8
- return {
9
- frontmatter: data,
10
- content: body.trim(),
11
- raw: content
12
- };
13
- }
14
- function extractSkillName(frontmatter, filename) {
15
- if (frontmatter.name) return frontmatter.name;
16
- if (filename) return filename.replace(/\.md$/i, "").replace(/^SKILL[._-]?/i, "").replace(/[._-]/g, " ").trim();
17
- return "Unnamed Skill";
18
- }
19
- function validateSkillMd(parsed) {
20
- const errors = [];
21
- if (!parsed.content) errors.push("Skill content is empty");
22
- if (parsed.frontmatter.globs) {
23
- const globs = Array.isArray(parsed.frontmatter.globs) ? parsed.frontmatter.globs : [parsed.frontmatter.globs];
24
- for (const glob of globs) if (typeof glob !== "string") errors.push(`Invalid glob pattern: ${glob}`);
25
- }
26
- return errors;
27
- }
28
-
29
- //#endregion
30
- //#region src/skill/discover.ts
31
- const DEFAULT_EXTENSIONS = [".md"];
32
- function discoverSkills(dir, options = {}) {
33
- const { recursive = false, extensions = DEFAULT_EXTENSIONS } = options;
34
- if (!existsSync(dir)) return [];
35
- const skills = [];
36
- const entries = readdirSync(dir);
37
- for (const entry of entries) {
38
- const fullPath = join(dir, entry);
39
- const stat = statSync(fullPath);
40
- if (stat.isDirectory() && recursive) skills.push(...discoverSkills(fullPath, options));
41
- else if (stat.isFile() && extensions.includes(extname(entry).toLowerCase())) try {
42
- const parsed = parseSkillMd(readFileSync(fullPath, "utf-8"));
43
- const filename = basename(entry, extname(entry));
44
- skills.push({
45
- path: fullPath,
46
- filename,
47
- name: extractSkillName(parsed.frontmatter, filename),
48
- parsed
49
- });
50
- } catch {}
51
- }
52
- return skills;
53
- }
54
- function filterSkills(skills, query) {
55
- const lowerQuery = query.toLowerCase();
56
- return skills.filter((skill) => {
57
- const nameMatch = skill.name.toLowerCase().includes(lowerQuery);
58
- const filenameMatch = skill.filename.toLowerCase().includes(lowerQuery);
59
- const tagMatch = skill.parsed.frontmatter.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery));
60
- return nameMatch || filenameMatch || tagMatch;
61
- });
62
- }
63
- function findSkillByName(skills, name) {
64
- const lowerName = name.toLowerCase();
65
- return skills.find((skill) => skill.name.toLowerCase() === lowerName || skill.filename.toLowerCase() === lowerName);
66
- }
67
-
68
- //#endregion
69
- export { parseSkillMd as a, extractSkillName as i, filterSkills as n, validateSkillMd as o, findSkillByName as r, discoverSkills as t };
File without changes
File without changes