agent-config-sync 0.1.0

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.
@@ -0,0 +1,129 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ // ============================================================================
4
+ // Constants
5
+ // ============================================================================
6
+ /** Native config file paths for each target relative to project root */
7
+ const TARGET_CONFIG_PATHS = {
8
+ claude: '.mcp.json',
9
+ codex: path.join('.codex', 'config.toml'),
10
+ gemini: path.join('.gemini', 'settings.json'),
11
+ };
12
+ /** Skills directory paths for each target relative to project root */
13
+ const TARGET_SKILLS_PATHS = {
14
+ claude: path.join('.claude', 'skills'),
15
+ codex: path.join('.codex', 'skills'),
16
+ gemini: path.join('.gemini', 'antigravity', 'skills'),
17
+ };
18
+ // ============================================================================
19
+ // Project Discovery
20
+ // ============================================================================
21
+ /**
22
+ * Discover the active project from the current working directory.
23
+ *
24
+ * Priority:
25
+ * 1. Nearest ancestor that is a Git repository root
26
+ * 2. Nearest ancestor containing any supported native config file
27
+ * 3. Fail with clear error
28
+ */
29
+ export async function discoverProject(cwd = process.cwd()) {
30
+ const gitRoot = await findGitRoot(cwd);
31
+ const projectRoot = gitRoot ?? (await findNativeConfigRoot(cwd));
32
+ if (!projectRoot) {
33
+ throw new Error('Not inside a managed project. ' +
34
+ 'Navigate to a Git repository or a directory with native agent config files.');
35
+ }
36
+ const targets = await resolveNativeConfigPaths(projectRoot);
37
+ return { root: projectRoot, targets };
38
+ }
39
+ /**
40
+ * Find the nearest Git repository root by traversing upwards.
41
+ */
42
+ async function findGitRoot(cwd) {
43
+ let current = path.resolve(cwd);
44
+ while (true) {
45
+ const gitDir = path.join(current, '.git');
46
+ try {
47
+ const stat = await fs.stat(gitDir);
48
+ if (stat.isDirectory()) {
49
+ return current;
50
+ }
51
+ }
52
+ catch {
53
+ // .git doesn't exist, continue
54
+ }
55
+ const parent = path.dirname(current);
56
+ if (parent === current) {
57
+ // Reached root
58
+ return null;
59
+ }
60
+ current = parent;
61
+ }
62
+ }
63
+ /**
64
+ * Find the nearest ancestor containing any supported native config file.
65
+ */
66
+ async function findNativeConfigRoot(cwd) {
67
+ let current = path.resolve(cwd);
68
+ while (true) {
69
+ for (const targetPath of Object.values(TARGET_CONFIG_PATHS)) {
70
+ const fullPath = path.join(current, targetPath);
71
+ try {
72
+ await fs.access(fullPath);
73
+ return current;
74
+ }
75
+ catch {
76
+ // File doesn't exist, try next
77
+ }
78
+ }
79
+ const parent = path.dirname(current);
80
+ if (parent === current) {
81
+ // Reached root
82
+ return null;
83
+ }
84
+ current = parent;
85
+ }
86
+ }
87
+ /**
88
+ * Resolve native config paths for all targets relative to the project root.
89
+ */
90
+ async function resolveNativeConfigPaths(projectRoot) {
91
+ const targets = new Map();
92
+ for (const [target, relativePath] of Object.entries(TARGET_CONFIG_PATHS)) {
93
+ const fullPath = path.join(projectRoot, relativePath);
94
+ let exists = false;
95
+ try {
96
+ await fs.access(fullPath);
97
+ exists = true;
98
+ }
99
+ catch {
100
+ // File doesn't exist
101
+ }
102
+ targets.set(target, {
103
+ target,
104
+ path: fullPath,
105
+ exists,
106
+ });
107
+ }
108
+ return targets;
109
+ }
110
+ // ============================================================================
111
+ // Utilities
112
+ // ============================================================================
113
+ /**
114
+ * Get the relative path from project root to a config file.
115
+ */
116
+ export function getRelativeConfigPath(projectRoot, target) {
117
+ return TARGET_CONFIG_PATHS[target];
118
+ }
119
+ /**
120
+ * Get the relative path from project root to the skills directory.
121
+ */
122
+ export function getSkillsPath(projectRoot, target) {
123
+ return path.join(projectRoot, TARGET_SKILLS_PATHS[target]);
124
+ }
125
+ /**
126
+ * Get the skills directory paths constant.
127
+ */
128
+ export { TARGET_SKILLS_PATHS };
129
+ //# sourceMappingURL=project-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-discovery.js","sourceRoot":"","sources":["../src/project-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Search the skills.directory registry for skills.
3
+ */
4
+ export declare function searchSkills(query: string): Promise<RegistrySkill[]>;
5
+ /**
6
+ * Get detailed information about a skill from the registry.
7
+ */
8
+ export declare function getSkillInfo(skillName: string): Promise<RegistrySkillInfo | null>;
9
+ /**
10
+ * Download a skill's SKILL.md content from GitHub.
11
+ */
12
+ export declare function downloadSkillContent(githubUrl: string): Promise<string>;
13
+ /**
14
+ * Install a skill from GitHub URL.
15
+ */
16
+ export declare function installFromGitHub(githubUrl: string, skillName?: string): Promise<{
17
+ name: string;
18
+ content: string;
19
+ }>;
20
+ export interface RegistrySkill {
21
+ name: string;
22
+ description: string;
23
+ version: string;
24
+ author: string;
25
+ verticals: string[];
26
+ stars: number;
27
+ installs: number;
28
+ repo: string;
29
+ skill_md: string;
30
+ }
31
+ export interface RegistrySkillInfo {
32
+ name: string;
33
+ description: string;
34
+ author: string;
35
+ verticals: string[];
36
+ updated: string;
37
+ stars: number;
38
+ installs: number;
39
+ repo: string;
40
+ skill_md: string;
41
+ installation: {
42
+ local: boolean;
43
+ global: string;
44
+ };
45
+ links: {
46
+ repo: string;
47
+ skill_md: string;
48
+ };
49
+ }
@@ -0,0 +1,121 @@
1
+ // ============================================================================
2
+ // Skills Directory Registry Client
3
+ // ============================================================================
4
+ /** Maximum size for skill content (1MB) */
5
+ const MAX_SKILL_SIZE = 1024 * 1024;
6
+ /**
7
+ * Validate that a URL is from GitHub.
8
+ */
9
+ function validateGitHubUrl(inputUrl) {
10
+ try {
11
+ const url = new URL(inputUrl);
12
+ const allowedHosts = ['github.com', 'raw.githubusercontent.com', 'www.github.com'];
13
+ return allowedHosts.includes(url.hostname);
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ /**
20
+ * Search the skills.directory registry for skills.
21
+ */
22
+ export async function searchSkills(query) {
23
+ const url = `https://api.skills-directory.com/v1/search?q=${encodeURIComponent(query)}`;
24
+ try {
25
+ const response = await fetch(url);
26
+ if (!response.ok) {
27
+ throw new Error('Registry search failed');
28
+ }
29
+ const data = await response.json();
30
+ return data.skills || [];
31
+ }
32
+ catch (error) {
33
+ console.error('Failed to search registry. Please try again later.');
34
+ return [];
35
+ }
36
+ }
37
+ /**
38
+ * Get detailed information about a skill from the registry.
39
+ */
40
+ export async function getSkillInfo(skillName) {
41
+ const url = `https://api.skills-directory.com/v1/skills/${encodeURIComponent(skillName)}`;
42
+ try {
43
+ const response = await fetch(url);
44
+ if (!response.ok) {
45
+ if (response.status === 404) {
46
+ return null;
47
+ }
48
+ throw new Error('Registry fetch failed');
49
+ }
50
+ const data = await response.json();
51
+ return data;
52
+ }
53
+ catch (error) {
54
+ console.error('Failed to fetch skill info. Please try again later.');
55
+ return null;
56
+ }
57
+ }
58
+ /**
59
+ * Download a skill's SKILL.md content from GitHub.
60
+ */
61
+ export async function downloadSkillContent(githubUrl) {
62
+ // Validate URL is from GitHub
63
+ if (!validateGitHubUrl(githubUrl)) {
64
+ throw new Error('Only GitHub URLs are allowed');
65
+ }
66
+ // Parse GitHub URL to extract raw content URL
67
+ const rawUrl = githubUrl.replace('github.com', 'raw.githubusercontent.com')
68
+ .replace('/blob/', '/')
69
+ .replace('/tree/', '/');
70
+ // Ensure we point to SKILL.md
71
+ const skillUrl = rawUrl.endsWith('SKILL.md') ? rawUrl : `${rawUrl}/SKILL.md`;
72
+ // Validate final URL is still from GitHub
73
+ if (!validateGitHubUrl(skillUrl)) {
74
+ throw new Error('Invalid GitHub URL structure');
75
+ }
76
+ try {
77
+ const response = await fetch(skillUrl);
78
+ if (!response.ok) {
79
+ throw new Error(`Download failed: ${response.status}`);
80
+ }
81
+ // Check content length
82
+ const contentLength = response.headers.get('content-length');
83
+ if (contentLength && parseInt(contentLength, 10) > MAX_SKILL_SIZE) {
84
+ throw new Error('Skill file too large');
85
+ }
86
+ const content = await response.text();
87
+ // Double-check actual content size
88
+ if (content.length > MAX_SKILL_SIZE) {
89
+ throw new Error('Skill file too large');
90
+ }
91
+ return content;
92
+ }
93
+ catch (error) {
94
+ const message = error instanceof Error ? error.message : 'Unknown error';
95
+ if (message.includes('Only GitHub') || message.includes('Invalid GitHub')) {
96
+ throw new Error(message);
97
+ }
98
+ throw new Error(`Failed to download skill. Check the URL and try again.`);
99
+ }
100
+ }
101
+ /**
102
+ * Install a skill from GitHub URL.
103
+ */
104
+ export async function installFromGitHub(githubUrl, skillName) {
105
+ // Fetch the repository info to get the default branch if needed
106
+ const content = await downloadSkillContent(githubUrl);
107
+ // Parse the skill name from the content if not provided
108
+ if (!skillName) {
109
+ const nameMatch = content.match(/^name:\s*(.+)$/m);
110
+ if (nameMatch) {
111
+ skillName = nameMatch[1].trim().replace(/^["']|["']$/g, '');
112
+ }
113
+ else {
114
+ // Extract from GitHub URL
115
+ const urlMatch = githubUrl.match(/\/skills\/([^\/]+)/);
116
+ skillName = urlMatch ? urlMatch[1] : 'unknown-skill';
117
+ }
118
+ }
119
+ return { name: skillName, content };
120
+ }
121
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { TargetName } from './types.js';
2
+ /**
3
+ * Validate a skill name to prevent path traversal and injection.
4
+ */
5
+ export declare function validateSkillName(skillName: string): void;
6
+ /**
7
+ * Get the skills directory path for a target.
8
+ * - Claude: <project>/.claude/skills/
9
+ * - Codex: <project>/.codex/skills/
10
+ * - Gemini: <project>/.gemini/antigravity/skills/
11
+ */
12
+ export declare function getSkillsDir(projectRoot: string, target: TargetName): string;
13
+ /**
14
+ * Get the skill directory path for a specific skill.
15
+ */
16
+ export declare function getSkillDir(projectRoot: string, target: TargetName, skillName: string): string;
17
+ /**
18
+ * Get the SKILL.md file path for a specific skill.
19
+ */
20
+ export declare function getSkillFilePath(projectRoot: string, target: TargetName, skillName: string): string;
21
+ /**
22
+ * Read a skill file from disk.
23
+ */
24
+ export declare function readSkill(projectRoot: string, target: TargetName, skillName: string): Promise<{
25
+ exists: boolean;
26
+ content: string | null;
27
+ }>;
28
+ /**
29
+ * Write a skill file to disk atomically.
30
+ */
31
+ export declare function writeSkill(projectRoot: string, target: TargetName, skillName: string, content: string): Promise<void>;
32
+ /**
33
+ * Remove a skill directory.
34
+ */
35
+ export declare function removeSkill(projectRoot: string, target: TargetName, skillName: string): Promise<boolean>;
36
+ /**
37
+ * Add a skill to a project.
38
+ */
39
+ export declare function addSkillToConfig(projectRoot: string, target: TargetName, skillId: string, content: string): Promise<void>;
40
+ /**
41
+ * Remove a skill from a project.
42
+ */
43
+ export declare function removeSkillFromConfig(projectRoot: string, target: TargetName, skillName: string): Promise<void>;
44
+ /**
45
+ * Get all skills from a project's native config directories.
46
+ */
47
+ export declare function getSkills(projectRoot: string, target: TargetName): Promise<Record<string, {
48
+ enabled: boolean;
49
+ }>>;
50
+ /**
51
+ * Check if a skill is enabled for a target.
52
+ */
53
+ export declare function isSkillEnabled(projectRoot: string, target: TargetName, skillName: string): Promise<boolean>;
@@ -0,0 +1,183 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ // ============================================================================
4
+ // Constants
5
+ // ============================================================================
6
+ /** Maximum size for skill files (1MB) */
7
+ const MAX_SKILL_SIZE = 1024 * 1024;
8
+ /** Valid skill name pattern: alphanumeric, hyphens, underscores, dots */
9
+ const SKILL_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
10
+ // ============================================================================
11
+ // Validation
12
+ // ============================================================================
13
+ /**
14
+ * Validate a skill name to prevent path traversal and injection.
15
+ */
16
+ export function validateSkillName(skillName) {
17
+ if (!skillName || skillName.length === 0) {
18
+ throw new Error('Skill name cannot be empty');
19
+ }
20
+ if (skillName.length > 100) {
21
+ throw new Error('Skill name too long (max 100 characters)');
22
+ }
23
+ if (!SKILL_NAME_PATTERN.test(skillName)) {
24
+ throw new Error('Skill name must contain only alphanumeric characters, hyphens, underscores, and dots');
25
+ }
26
+ // Prevent path traversal
27
+ if (skillName.includes('..') || skillName.includes('/') || skillName.includes('\\')) {
28
+ throw new Error('Skill name cannot contain path traversal characters');
29
+ }
30
+ // Prevent leading/trailing dots and dashes (security issues)
31
+ if (skillName.startsWith('.') || skillName.startsWith('-') ||
32
+ skillName.endsWith('.') || skillName.endsWith('-')) {
33
+ throw new Error('Skill name cannot start or end with a dot or dash');
34
+ }
35
+ }
36
+ // ============================================================================
37
+ // Skill Path Resolution
38
+ // ============================================================================
39
+ /**
40
+ * Get the skills directory path for a target.
41
+ * - Claude: <project>/.claude/skills/
42
+ * - Codex: <project>/.codex/skills/
43
+ * - Gemini: <project>/.gemini/antigravity/skills/
44
+ */
45
+ export function getSkillsDir(projectRoot, target) {
46
+ switch (target) {
47
+ case 'claude':
48
+ return path.join(projectRoot, '.claude', 'skills');
49
+ case 'codex':
50
+ return path.join(projectRoot, '.codex', 'skills');
51
+ case 'gemini':
52
+ return path.join(projectRoot, '.gemini', 'antigravity', 'skills');
53
+ }
54
+ }
55
+ /**
56
+ * Get the skill directory path for a specific skill.
57
+ */
58
+ export function getSkillDir(projectRoot, target, skillName) {
59
+ const skillsDir = getSkillsDir(projectRoot, target);
60
+ return path.join(skillsDir, skillName);
61
+ }
62
+ /**
63
+ * Get the SKILL.md file path for a specific skill.
64
+ */
65
+ export function getSkillFilePath(projectRoot, target, skillName) {
66
+ const skillDir = getSkillDir(projectRoot, target, skillName);
67
+ return path.join(skillDir, 'SKILL.md');
68
+ }
69
+ // ============================================================================
70
+ // Skill File Operations
71
+ // ============================================================================
72
+ /**
73
+ * Read a skill file from disk.
74
+ */
75
+ export async function readSkill(projectRoot, target, skillName) {
76
+ validateSkillName(skillName);
77
+ const skillPath = getSkillFilePath(projectRoot, target, skillName);
78
+ try {
79
+ await fs.access(skillPath);
80
+ const content = await fs.readFile(skillPath, 'utf8');
81
+ // Check content size
82
+ if (content.length > MAX_SKILL_SIZE) {
83
+ throw new Error('Skill file too large');
84
+ }
85
+ return { exists: true, content };
86
+ }
87
+ catch {
88
+ return { exists: false, content: null };
89
+ }
90
+ }
91
+ /**
92
+ * Write a skill file to disk atomically.
93
+ */
94
+ export async function writeSkill(projectRoot, target, skillName, content) {
95
+ validateSkillName(skillName);
96
+ // Check content size before writing
97
+ if (content.length > MAX_SKILL_SIZE) {
98
+ throw new Error(`Skill content too large (${content.length} bytes, max ${MAX_SKILL_SIZE})`);
99
+ }
100
+ const skillDir = getSkillDir(projectRoot, target, skillName);
101
+ const skillPath = getSkillFilePath(projectRoot, target, skillName);
102
+ // Create skill directory if it doesn't exist
103
+ await fs.mkdir(skillDir, { recursive: true });
104
+ // Write atomically
105
+ const tempPath = `${skillPath}.tmp`;
106
+ await fs.writeFile(tempPath, content, 'utf8');
107
+ await fs.rename(tempPath, skillPath);
108
+ }
109
+ /**
110
+ * Remove a skill directory.
111
+ */
112
+ export async function removeSkill(projectRoot, target, skillName) {
113
+ validateSkillName(skillName);
114
+ const skillDir = getSkillDir(projectRoot, target, skillName);
115
+ try {
116
+ await fs.access(skillDir);
117
+ }
118
+ catch {
119
+ return false; // Doesn't exist
120
+ }
121
+ await fs.rm(skillDir, { recursive: true, force: true });
122
+ return true;
123
+ }
124
+ /**
125
+ * Add a skill to a project.
126
+ */
127
+ export async function addSkillToConfig(projectRoot, target, skillId, content) {
128
+ await writeSkill(projectRoot, target, skillId, content);
129
+ }
130
+ /**
131
+ * Remove a skill from a project.
132
+ */
133
+ export async function removeSkillFromConfig(projectRoot, target, skillName) {
134
+ await removeSkill(projectRoot, target, skillName);
135
+ }
136
+ // ============================================================================
137
+ // Skill Status Queries
138
+ // ============================================================================
139
+ /**
140
+ * Get all skills from a project's native config directories.
141
+ */
142
+ export async function getSkills(projectRoot, target) {
143
+ const skillsDir = getSkillsDir(projectRoot, target);
144
+ const skills = {};
145
+ try {
146
+ await fs.access(skillsDir);
147
+ }
148
+ catch {
149
+ return skills; // Directory doesn't exist
150
+ }
151
+ const entries = await fs.readdir(skillsDir, { withFileTypes: true });
152
+ for (const entry of entries) {
153
+ if (!entry.isDirectory())
154
+ continue;
155
+ const skillName = entry.name;
156
+ // Validate skill name to skip invalid entries
157
+ try {
158
+ validateSkillName(skillName);
159
+ }
160
+ catch {
161
+ // Skip invalid skill names
162
+ continue;
163
+ }
164
+ const skillPath = path.join(skillsDir, skillName, 'SKILL.md');
165
+ try {
166
+ await fs.access(skillPath);
167
+ // All skills are considered "enabled" if the SKILL.md file exists
168
+ skills[skillName] = { enabled: true };
169
+ }
170
+ catch {
171
+ // SKILL.md doesn't exist, skip
172
+ }
173
+ }
174
+ return skills;
175
+ }
176
+ /**
177
+ * Check if a skill is enabled for a target.
178
+ */
179
+ export async function isSkillEnabled(projectRoot, target, skillName) {
180
+ const { exists } = await readSkill(projectRoot, target, skillName);
181
+ return exists;
182
+ }
183
+ //# sourceMappingURL=skill-adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-adapters.js","sourceRoot":"","sources":["../src/skill-adapters.ts"],"names":[],"mappings":"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"}