agent-dir 0.0.0-stage → 0.1.2

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -2
  3. package/dist/bin/agent-dir.d.ts +3 -0
  4. package/dist/bin/agent-dir.d.ts.map +1 -0
  5. package/dist/bin/agent-dir.js +201 -0
  6. package/dist/bin/agent-dir.js.map +1 -0
  7. package/dist/src/auth.d.ts +3 -0
  8. package/dist/src/auth.d.ts.map +1 -0
  9. package/dist/src/auth.js +18 -0
  10. package/dist/src/auth.js.map +1 -0
  11. package/dist/src/config.d.ts +6 -0
  12. package/dist/src/config.d.ts.map +1 -0
  13. package/dist/src/config.js +80 -0
  14. package/dist/src/config.js.map +1 -0
  15. package/dist/src/logging.d.ts +16 -0
  16. package/dist/src/logging.d.ts.map +1 -0
  17. package/dist/src/logging.js +243 -0
  18. package/dist/src/logging.js.map +1 -0
  19. package/dist/src/mcp.d.ts +9 -0
  20. package/dist/src/mcp.d.ts.map +1 -0
  21. package/dist/src/mcp.js +866 -0
  22. package/dist/src/mcp.js.map +1 -0
  23. package/dist/src/server.d.ts +11 -0
  24. package/dist/src/server.d.ts.map +1 -0
  25. package/dist/src/server.js +562 -0
  26. package/dist/src/server.js.map +1 -0
  27. package/dist/src/tools/code.d.ts +27 -0
  28. package/dist/src/tools/code.d.ts.map +1 -0
  29. package/dist/src/tools/code.js +147 -0
  30. package/dist/src/tools/code.js.map +1 -0
  31. package/dist/src/tools/commands.d.ts +8 -0
  32. package/dist/src/tools/commands.d.ts.map +1 -0
  33. package/dist/src/tools/commands.js +61 -0
  34. package/dist/src/tools/commands.js.map +1 -0
  35. package/dist/src/tools/diagnostics.d.ts +9 -0
  36. package/dist/src/tools/diagnostics.d.ts.map +1 -0
  37. package/dist/src/tools/diagnostics.js +83 -0
  38. package/dist/src/tools/diagnostics.js.map +1 -0
  39. package/dist/src/tools/files.d.ts +39 -0
  40. package/dist/src/tools/files.d.ts.map +1 -0
  41. package/dist/src/tools/files.js +142 -0
  42. package/dist/src/tools/files.js.map +1 -0
  43. package/dist/src/tools/git.d.ts +15 -0
  44. package/dist/src/tools/git.d.ts.map +1 -0
  45. package/dist/src/tools/git.js +104 -0
  46. package/dist/src/tools/git.js.map +1 -0
  47. package/dist/src/tools/metadata.d.ts +20 -0
  48. package/dist/src/tools/metadata.d.ts.map +1 -0
  49. package/dist/src/tools/metadata.js +101 -0
  50. package/dist/src/tools/metadata.js.map +1 -0
  51. package/dist/src/tools/npm.d.ts +3 -0
  52. package/dist/src/tools/npm.d.ts.map +1 -0
  53. package/dist/src/tools/npm.js +76 -0
  54. package/dist/src/tools/npm.js.map +1 -0
  55. package/dist/src/tools/skills.d.ts +33 -0
  56. package/dist/src/tools/skills.d.ts.map +1 -0
  57. package/dist/src/tools/skills.js +240 -0
  58. package/dist/src/tools/skills.js.map +1 -0
  59. package/dist/src/tunnel.d.ts +3 -0
  60. package/dist/src/tunnel.d.ts.map +1 -0
  61. package/dist/src/tunnel.js +114 -0
  62. package/dist/src/tunnel.js.map +1 -0
  63. package/dist/src/types.d.ts +45 -0
  64. package/dist/src/types.d.ts.map +1 -0
  65. package/dist/src/types.js +2 -0
  66. package/dist/src/types.js.map +1 -0
  67. package/package.json +51 -4
@@ -0,0 +1,15 @@
1
+ export interface GitResult {
2
+ command: string;
3
+ exitCode: number;
4
+ stdout: string;
5
+ stderr: string;
6
+ }
7
+ export declare function gitStage(root: string, paths: string[]): Promise<GitResult>;
8
+ export declare function gitUnstage(root: string, paths: string[]): Promise<GitResult>;
9
+ export declare function gitCommit(root: string, message: string): Promise<GitResult>;
10
+ export declare function gitPush(root: string, remote?: string, branch?: string): Promise<GitResult>;
11
+ export declare function gitStatus(root: string): Promise<GitResult>;
12
+ export declare function gitDiff(root: string, staged?: boolean, pathspec?: string): Promise<GitResult>;
13
+ export declare function gitRestore(root: string, paths: string[], staged?: boolean): Promise<GitResult>;
14
+ export declare function gitLog(root: string, limit?: number, pathspec?: string): Promise<GitResult>;
15
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../src/tools/git.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AA6CD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAG1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAG5E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAG3E;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAa1F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAE1D;AACD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,UAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAK3F;AACD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAG5F;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAatF"}
@@ -0,0 +1,104 @@
1
+ import { spawn } from "node:child_process";
2
+ function validatePaths(paths) {
3
+ if (!Array.isArray(paths) ||
4
+ paths.length === 0 ||
5
+ paths.some((path) => typeof path !== "string" || !path)) {
6
+ throw new Error("paths must contain at least one non-empty path.");
7
+ }
8
+ }
9
+ function runGit(root, args, timeoutMs = 30000) {
10
+ return new Promise((resolve, reject) => {
11
+ const child = spawn("git", args, { cwd: root, shell: false, env: process.env });
12
+ let stdout = "";
13
+ let stderr = "";
14
+ let finished = false;
15
+ const timer = setTimeout(() => {
16
+ if (finished)
17
+ return;
18
+ finished = true;
19
+ child.kill("SIGTERM");
20
+ reject(new Error(`git ${args.join(" ")} timed out after ${timeoutMs}ms.`));
21
+ }, timeoutMs);
22
+ child.stdout.on("data", (chunk) => {
23
+ stdout += chunk.toString();
24
+ });
25
+ child.stderr.on("data", (chunk) => {
26
+ stderr += chunk.toString();
27
+ });
28
+ child.on("error", (error) => {
29
+ if (finished)
30
+ return;
31
+ finished = true;
32
+ clearTimeout(timer);
33
+ reject(error);
34
+ });
35
+ child.on("close", (code) => {
36
+ if (finished)
37
+ return;
38
+ finished = true;
39
+ clearTimeout(timer);
40
+ resolve({ command: ["git", ...args].join(" "), exitCode: code ?? 1, stdout, stderr });
41
+ });
42
+ });
43
+ }
44
+ export function gitStage(root, paths) {
45
+ validatePaths(paths);
46
+ return runGit(root, ["add", "--", ...paths]);
47
+ }
48
+ export function gitUnstage(root, paths) {
49
+ validatePaths(paths);
50
+ return runGit(root, ["restore", "--staged", "--", ...paths]);
51
+ }
52
+ export function gitCommit(root, message) {
53
+ if (!message.trim())
54
+ throw new Error("commit message must not be empty.");
55
+ return runGit(root, ["commit", "-m", message]);
56
+ }
57
+ export function gitPush(root, remote, branch) {
58
+ const args = ["push"];
59
+ if (remote) {
60
+ if (!/^[A-Za-z0-9._-]+$/.test(remote))
61
+ throw new Error("Invalid Git remote name.");
62
+ args.push(remote);
63
+ if (branch) {
64
+ if (!/^[A-Za-z0-9._/-]+$/.test(branch))
65
+ throw new Error("Invalid Git branch name.");
66
+ args.push(branch);
67
+ }
68
+ }
69
+ else if (branch) {
70
+ throw new Error("A Git remote is required when specifying a branch.");
71
+ }
72
+ return runGit(root, args);
73
+ }
74
+ export function gitStatus(root) {
75
+ return runGit(root, ["status", "--short", "--branch"]);
76
+ }
77
+ export function gitDiff(root, staged = false, pathspec) {
78
+ const args = ["diff", "--no-ext-diff", "--no-color"];
79
+ if (staged)
80
+ args.push("--cached");
81
+ if (pathspec)
82
+ args.push("--", pathspec);
83
+ return runGit(root, args);
84
+ }
85
+ export function gitRestore(root, paths, staged = false) {
86
+ validatePaths(paths);
87
+ return runGit(root, ["restore", ...(staged ? ["--staged"] : []), "--", ...paths]);
88
+ }
89
+ export function gitLog(root, limit = 20, pathspec) {
90
+ if (!Number.isInteger(limit) || limit < 1 || limit > 200)
91
+ throw new Error("limit must be an integer between 1 and 200.");
92
+ const args = [
93
+ "log",
94
+ "--no-color",
95
+ "--date=iso-strict",
96
+ "--format=%H%x09%ad%x09%an%x09%s",
97
+ "-n",
98
+ String(limit),
99
+ ];
100
+ if (pathspec)
101
+ args.push("--", pathspec);
102
+ return runGit(root, args);
103
+ }
104
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/tools/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface ProjectOverview {
2
+ root: string;
3
+ files: number;
4
+ directories: number;
5
+ languages: Array<{
6
+ extension: string;
7
+ files: number;
8
+ }>;
9
+ packageManagers: string[];
10
+ projectFiles: string[];
11
+ git: {
12
+ available: boolean;
13
+ branch?: string;
14
+ clean?: boolean;
15
+ };
16
+ }
17
+ export declare function fileInfo(root: string, relativePath: string): Promise<Record<string, unknown>>;
18
+ export declare function packageInfo(root: string): Promise<Record<string, unknown>>;
19
+ export declare function projectOverview(root: string): Promise<ProjectOverview>;
20
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/tools/metadata.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/D;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoBlC;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAuBhF;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAuD5E"}
@@ -0,0 +1,101 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { listFiles } from "./files.js";
4
+ import { gitStatus } from "./git.js";
5
+ export async function fileInfo(root, relativePath) {
6
+ const resolvedRoot = await fs.realpath(root);
7
+ const target = path.resolve(resolvedRoot, relativePath);
8
+ if (target !== resolvedRoot && !target.startsWith(`${resolvedRoot}${path.sep}`))
9
+ throw new Error("Path escapes the exposed project directory.");
10
+ const stat = await fs.lstat(target);
11
+ return {
12
+ path: relativePath,
13
+ type: stat.isDirectory()
14
+ ? "directory"
15
+ : stat.isFile()
16
+ ? "file"
17
+ : stat.isSymbolicLink()
18
+ ? "symlink"
19
+ : "other",
20
+ size: stat.size,
21
+ mode: (stat.mode & 0o777).toString(8),
22
+ modifiedAt: stat.mtime.toISOString(),
23
+ createdAt: stat.birthtime.toISOString(),
24
+ };
25
+ }
26
+ export async function packageInfo(root) {
27
+ const packagePath = path.join(await fs.realpath(root), "package.json");
28
+ try {
29
+ const packageJson = JSON.parse(await fs.readFile(packagePath, "utf8"));
30
+ return {
31
+ path: "package.json",
32
+ name: packageJson.name ?? null,
33
+ version: packageJson.version ?? null,
34
+ description: packageJson.description ?? null,
35
+ packageManager: packageJson.packageManager ?? null,
36
+ engines: packageJson.engines ?? null,
37
+ scripts: packageJson.scripts ?? {},
38
+ dependencies: packageJson.dependencies ?? {},
39
+ devDependencies: packageJson.devDependencies ?? {},
40
+ };
41
+ }
42
+ catch (error) {
43
+ if (error.code === "ENOENT")
44
+ return { path: "package.json", exists: false };
45
+ throw new Error("package.json exists but is not valid JSON.");
46
+ }
47
+ }
48
+ export async function projectOverview(root) {
49
+ const entries = await listFiles(root);
50
+ const files = entries.filter((entry) => entry.type === "file");
51
+ const directories = entries.filter((entry) => entry.type === "directory");
52
+ const counts = new Map();
53
+ for (const file of files) {
54
+ const extension = path.extname(file.path).toLowerCase() || "[no extension]";
55
+ counts.set(extension, (counts.get(extension) ?? 0) + 1);
56
+ }
57
+ const rootEntries = await fs.readdir(await fs.realpath(root));
58
+ const packageManagerPairs = [
59
+ ["package-lock.json", "npm"],
60
+ ["pnpm-lock.yaml", "pnpm"],
61
+ ["yarn.lock", "yarn"],
62
+ ["bun.lock", "bun"],
63
+ ["bun.lockb", "bun"],
64
+ ["Cargo.lock", "cargo"],
65
+ ["go.sum", "go"],
66
+ ["poetry.lock", "poetry"],
67
+ ["uv.lock", "uv"],
68
+ ];
69
+ const packageManagers = packageManagerPairs
70
+ .filter(([file]) => rootEntries.includes(file))
71
+ .map(([, manager]) => manager);
72
+ const projectFiles = rootEntries.filter((name) => /^(README|LICENSE|CONTRIBUTING|CHANGELOG)(?:\.|$)|^(package|tsconfig|pyproject|Cargo|go\.mod)/i.test(name));
73
+ let git = { available: false };
74
+ try {
75
+ const status = await gitStatus(root);
76
+ if (status.exitCode === 0) {
77
+ const firstLine = status.stdout.split(/\r?\n/)[0] ?? "";
78
+ const branch = firstLine.match(/^##\s+(.+?)(?:\.\.\.|$)/)?.[1];
79
+ git = {
80
+ available: true,
81
+ ...(branch ? { branch } : {}),
82
+ clean: status.stdout.split(/\r?\n/).filter(Boolean).length <= 1,
83
+ };
84
+ }
85
+ }
86
+ catch {
87
+ git = { available: false };
88
+ }
89
+ return {
90
+ root,
91
+ files: files.length,
92
+ directories: directories.length,
93
+ languages: [...counts.entries()]
94
+ .sort((a, b) => b[1] - a[1])
95
+ .map(([extension, count]) => ({ extension, files: count })),
96
+ packageManagers,
97
+ projectFiles,
98
+ git,
99
+ };
100
+ }
101
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/tools/metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CommandResult } from "./commands.js";
2
+ export declare function runNpm(root: string, script: string, timeoutMs?: number, allowedScripts?: string[], signal?: AbortSignal): Promise<CommandResult>;
3
+ //# sourceMappingURL=npm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../../src/tools/npm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAMnD,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,SAAS,EAClB,cAAc,GAAE,MAAM,EAAO,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAqExB"}
@@ -0,0 +1,76 @@
1
+ import { spawn } from "node:child_process";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ export function runNpm(root, script, timeoutMs = 120000, allowedScripts = [], signal) {
5
+ if (!/^[A-Za-z0-9:_-]+$/.test(script))
6
+ throw new Error("Invalid npm script name.");
7
+ return (async () => {
8
+ let packageJson;
9
+ try {
10
+ packageJson = JSON.parse(await fs.readFile(path.join(root, "package.json"), "utf8"));
11
+ }
12
+ catch {
13
+ throw new Error("No valid package.json found.");
14
+ }
15
+ if (!packageJson.scripts?.[script])
16
+ throw new Error(`npm script '${script}' does not exist in package.json.`);
17
+ if (!allowedScripts.includes(script))
18
+ throw new Error(`npm script '${script}' is not allowed by this profile.`);
19
+ return new Promise((resolve, reject) => {
20
+ const executable = process.platform === "win32" ? "npm.cmd" : "npm";
21
+ const child = spawn(executable, ["run", script], {
22
+ cwd: root,
23
+ shell: false,
24
+ env: process.env,
25
+ });
26
+ let stdout = "";
27
+ let stderr = "";
28
+ let finished = false;
29
+ let timer;
30
+ const abort = () => {
31
+ if (finished)
32
+ return;
33
+ finished = true;
34
+ clearTimeout(timer);
35
+ child.kill("SIGTERM");
36
+ reject(new Error(`npm run ${script} was aborted.`));
37
+ };
38
+ timer = setTimeout(() => {
39
+ if (finished)
40
+ return;
41
+ finished = true;
42
+ child.kill("SIGTERM");
43
+ signal?.removeEventListener("abort", abort);
44
+ reject(new Error(`npm run ${script} timed out after ${timeoutMs}ms.`));
45
+ }, timeoutMs);
46
+ if (signal?.aborted) {
47
+ abort();
48
+ return;
49
+ }
50
+ signal?.addEventListener("abort", abort, { once: true });
51
+ child.stdout.on("data", (chunk) => {
52
+ stdout += chunk.toString();
53
+ });
54
+ child.stderr.on("data", (chunk) => {
55
+ stderr += chunk.toString();
56
+ });
57
+ child.on("error", (error) => {
58
+ if (!finished) {
59
+ finished = true;
60
+ clearTimeout(timer);
61
+ signal?.removeEventListener("abort", abort);
62
+ reject(error);
63
+ }
64
+ });
65
+ child.on("close", (code) => {
66
+ if (!finished) {
67
+ finished = true;
68
+ clearTimeout(timer);
69
+ signal?.removeEventListener("abort", abort);
70
+ resolve({ command: `npm run ${script}`, exitCode: code ?? 1, stdout, stderr });
71
+ }
72
+ });
73
+ });
74
+ })();
75
+ }
76
+ //# sourceMappingURL=npm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.js","sourceRoot":"","sources":["../../../src/tools/npm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ export interface SkillResource {
2
+ uri: string;
3
+ digest: string;
4
+ size: number;
5
+ }
6
+ export interface SkillEntry {
7
+ uri: string;
8
+ frontmatter: Record<string, unknown> & {
9
+ name: string;
10
+ description: string;
11
+ };
12
+ resources: SkillResource[] | "dynamic";
13
+ }
14
+ export declare function listSkills(root: string): Promise<SkillEntry[]>;
15
+ export declare function getSkill(root: string, uri: string): Promise<SkillEntry>;
16
+ export declare function readSkillResource(root: string, uri: string): Promise<{
17
+ uri: string;
18
+ mimeType: string;
19
+ text?: string;
20
+ blob?: string;
21
+ }>;
22
+ export declare function readSkillDirectory(root: string, uri: string): Promise<Array<{
23
+ uri: string;
24
+ name: string;
25
+ mimeType: string;
26
+ }>>;
27
+ export declare function listSkillResources(root: string): Promise<Array<{
28
+ uri: string;
29
+ name: string;
30
+ mimeType: string;
31
+ }>>;
32
+ export declare function skillResourceUrisForPath(root: string, relativePath: string): Promise<string[]>;
33
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/tools/skills.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,SAAS,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC;CACxC;AAMD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAEpE;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAI7E;AACD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAY1E;AACD,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAmCjE;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAQjE;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAUnB"}
@@ -0,0 +1,240 @@
1
+ import { createHash } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ import { listFiles } from "./files.js";
5
+ const SKILL_ROOTS = ["skills", ".agents/skills", ".claude/skills", ".github/skills"];
6
+ const MAX_RESOURCES = 512;
7
+ const MAX_TOTAL_BYTES = 16 * 1024 * 1024;
8
+ const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
9
+ const MAX_SKILL_NAME_LENGTH = 64;
10
+ const MAX_DESCRIPTION_LENGTH = 1024;
11
+ export async function listSkills(root) {
12
+ return (await discoverSkills(root)).map((skill) => skill.entry);
13
+ }
14
+ export async function getSkill(root, uri) {
15
+ const skill = (await discoverSkills(root)).find((item) => item.entry.uri === uri);
16
+ if (!skill)
17
+ throw new Error(`Unknown skill: ${uri}`);
18
+ return skill.entry;
19
+ }
20
+ export async function readSkillResource(root, uri) {
21
+ for (const skill of await discoverSkills(root)) {
22
+ const filePath = skill.resources.get(uri);
23
+ if (!filePath)
24
+ continue;
25
+ const bytes = await fs.readFile(filePath);
26
+ const text = bytes.toString("utf8");
27
+ if (Buffer.from(text, "utf8").equals(bytes)) {
28
+ return { uri, mimeType: mimeTypeFor(filePath), text };
29
+ }
30
+ return { uri, mimeType: mimeTypeFor(filePath), blob: bytes.toString("base64") };
31
+ }
32
+ throw new Error(`Unknown skill resource: ${uri}`);
33
+ }
34
+ export async function readSkillDirectory(root, uri) {
35
+ const prefix = "skill://";
36
+ if (!uri.startsWith(prefix) || uri.endsWith("/") || uri === prefix)
37
+ throw new Error(`Unknown skill directory: ${uri}`);
38
+ const relativeDirectory = uri.slice(prefix.length);
39
+ const resolvedRoot = await fs.realpath(root);
40
+ for (const skillRoot of SKILL_ROOTS) {
41
+ const directoryPath = path.join(resolvedRoot, skillRoot, ...relativeDirectory.split("/"));
42
+ const candidate = path.resolve(directoryPath);
43
+ if (candidate !== resolvedRoot && !candidate.startsWith(`${resolvedRoot}${path.sep}`))
44
+ continue;
45
+ try {
46
+ const resolvedDirectory = await fs.realpath(candidate);
47
+ if (resolvedDirectory !== resolvedRoot &&
48
+ !resolvedDirectory.startsWith(`${resolvedRoot}${path.sep}`))
49
+ continue;
50
+ const stats = await fs.stat(resolvedDirectory);
51
+ if (!stats.isDirectory())
52
+ continue;
53
+ const entries = await fs.readdir(resolvedDirectory, { withFileTypes: true });
54
+ return entries
55
+ .map((entry) => {
56
+ const childUri = `${uri}/${entry.name}`;
57
+ return {
58
+ uri: childUri,
59
+ name: entry.name,
60
+ mimeType: entry.isDirectory() ? "inode/directory" : mimeTypeFor(entry.name),
61
+ };
62
+ })
63
+ .sort((a, b) => a.uri.localeCompare(b.uri));
64
+ }
65
+ catch (error) {
66
+ if (error.code !== "ENOENT")
67
+ throw error;
68
+ }
69
+ }
70
+ throw new Error(`Unknown skill directory: ${uri}`);
71
+ }
72
+ export async function listSkillResources(root) {
73
+ return (await discoverSkills(root)).flatMap((skill) => [...skill.resources.entries()].map(([uri, filePath]) => ({
74
+ uri,
75
+ name: path.basename(filePath),
76
+ mimeType: mimeTypeFor(filePath),
77
+ })));
78
+ }
79
+ export async function skillResourceUrisForPath(root, relativePath) {
80
+ const resolvedRoot = await fs.realpath(root);
81
+ const target = path.resolve(resolvedRoot, relativePath);
82
+ const matches = new Set();
83
+ for (const skill of await discoverSkills(root)) {
84
+ for (const [uri, filePath] of skill.resources) {
85
+ if (path.resolve(filePath) === target)
86
+ matches.add(uri);
87
+ }
88
+ }
89
+ return [...matches].sort();
90
+ }
91
+ async function discoverSkills(root) {
92
+ const resolvedRoot = await fs.realpath(root);
93
+ const entries = await listFiles(root);
94
+ const skills = [];
95
+ for (const skillFile of entries.filter((entry) => entry.type === "file" && path.basename(entry.path) === "SKILL.md")) {
96
+ const normalizedSkillFile = skillFile.path.replaceAll(path.sep, "/");
97
+ const skillRoot = SKILL_ROOTS.find((rootName) => normalizedSkillFile === `${rootName}/SKILL.md` ||
98
+ normalizedSkillFile.startsWith(`${rootName}/`));
99
+ if (!skillRoot)
100
+ continue;
101
+ const skillPath = path.posix.dirname(normalizedSkillFile);
102
+ const relativeSkillPath = path.posix.relative(skillRoot, skillPath);
103
+ if (!relativeSkillPath ||
104
+ relativeSkillPath === "." ||
105
+ relativeSkillPath.startsWith("../") ||
106
+ path.posix.isAbsolute(relativeSkillPath))
107
+ continue;
108
+ const markdown = await fs.readFile(path.join(resolvedRoot, skillFile.path), "utf8");
109
+ const parsedFrontmatter = parseFrontmatter(markdown);
110
+ if (!parsedFrontmatter)
111
+ continue;
112
+ const nameValue = parsedFrontmatter.values.name;
113
+ const descriptionValue = parsedFrontmatter.values.description;
114
+ if (typeof nameValue !== "string" || typeof descriptionValue !== "string")
115
+ continue;
116
+ if (nameValue !== path.posix.basename(skillPath) ||
117
+ !SKILL_NAME_PATTERN.test(nameValue) ||
118
+ nameValue.length > MAX_SKILL_NAME_LENGTH ||
119
+ descriptionValue.length === 0 ||
120
+ descriptionValue.length > MAX_DESCRIPTION_LENGTH)
121
+ continue;
122
+ const skillUriPath = relativeSkillPath;
123
+ const skillUri = `skill://${skillUriPath}`;
124
+ const resources = [];
125
+ const resourceMap = new Map();
126
+ const descendants = entries.filter((entry) => entry.type === "file" &&
127
+ (entry.path === skillFile.path ||
128
+ entry.path.replaceAll(path.sep, "/").startsWith(`${skillPath}/`)));
129
+ if (descendants.length > MAX_RESOURCES)
130
+ continue;
131
+ let totalBytes = 0;
132
+ for (const resource of descendants) {
133
+ const normalizedResourcePath = resource.path.replaceAll(path.sep, "/");
134
+ const relative = path.posix.relative(skillPath, normalizedResourcePath);
135
+ const uri = `${skillUri}/${relative}`;
136
+ const filePath = path.join(resolvedRoot, resource.path);
137
+ const bytes = await fs.readFile(filePath);
138
+ totalBytes += bytes.length;
139
+ if (totalBytes > MAX_TOTAL_BYTES) {
140
+ totalBytes = -1;
141
+ break;
142
+ }
143
+ resources.push({
144
+ uri,
145
+ digest: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
146
+ size: bytes.length,
147
+ });
148
+ resourceMap.set(uri, filePath);
149
+ }
150
+ if (totalBytes < 0 || resources.length === 0)
151
+ continue;
152
+ resources.sort((a, b) => a.uri.localeCompare(b.uri));
153
+ skills.push({
154
+ entry: {
155
+ uri: `${skillUri}/SKILL.md`,
156
+ frontmatter: parsedFrontmatter.values,
157
+ resources,
158
+ },
159
+ resources: resourceMap,
160
+ });
161
+ }
162
+ return skills.sort((a, b) => a.entry.uri.localeCompare(b.entry.uri));
163
+ }
164
+ function parseFrontmatter(markdown) {
165
+ const lines = markdown.split(/\r?\n/);
166
+ if ((lines[0] ?? "").trim() !== "---")
167
+ return undefined;
168
+ const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
169
+ if (end < 0)
170
+ return undefined;
171
+ const result = {};
172
+ for (let index = 1; index < end; index += 1) {
173
+ const line = lines[index] ?? "";
174
+ if (!line.trim())
175
+ continue;
176
+ const match = line.match(/^([A-Za-z0-9_.-]+):\s*(.*)$/);
177
+ if (!match)
178
+ return undefined;
179
+ const key = match[1] ?? "";
180
+ if (Object.hasOwn(result, key))
181
+ return undefined;
182
+ const raw = match[2] ?? "";
183
+ result[key] = parseScalar(raw);
184
+ }
185
+ return { raw: lines.slice(1, end).join("\n"), values: result };
186
+ }
187
+ function parseScalar(value) {
188
+ const trimmed = value.trim();
189
+ if (!trimmed)
190
+ return "";
191
+ if (trimmed === "true")
192
+ return true;
193
+ if (trimmed === "false")
194
+ return false;
195
+ if (trimmed === "null")
196
+ return null;
197
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed))
198
+ return Number(trimmed);
199
+ if ((trimmed.startsWith("[") && trimmed.endsWith("]")) ||
200
+ (trimmed.startsWith("{") && trimmed.endsWith("}"))) {
201
+ try {
202
+ return JSON.parse(trimmed);
203
+ }
204
+ catch {
205
+ return trimmed;
206
+ }
207
+ }
208
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
209
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
210
+ return trimmed.slice(1, -1);
211
+ }
212
+ return trimmed;
213
+ }
214
+ function mimeTypeFor(filePath) {
215
+ switch (path.extname(filePath).toLowerCase()) {
216
+ case ".md":
217
+ return "text/markdown";
218
+ case ".json":
219
+ return "application/json";
220
+ case ".yaml":
221
+ case ".yml":
222
+ return "text/yaml";
223
+ case ".js":
224
+ case ".mjs":
225
+ case ".cjs":
226
+ case ".ts":
227
+ case ".mts":
228
+ case ".cts":
229
+ return "text/javascript";
230
+ case ".css":
231
+ return "text/css";
232
+ case ".html":
233
+ return "text/html";
234
+ case ".py":
235
+ return "text/x-python";
236
+ default:
237
+ return "text/plain";
238
+ }
239
+ }
240
+ //# sourceMappingURL=skills.js.map