@ian2018cs/agenthub 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian2018cs/agenthub",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "A web-based UI for AI Agents",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
package/server/index.js CHANGED
@@ -1135,6 +1135,15 @@ function handleCodexConnection(ws, userData) {
1135
1135
  codexEnv.OPENAI_BASE_URL = process.env.OPENAI_BASE_URL;
1136
1136
  }
1137
1137
 
1138
+ // Sync skills before starting codex (mirrors .claude/skills/ into .codex/skills/)
1139
+ if (userUuid) {
1140
+ try {
1141
+ await initCodexDirectories(userUuid);
1142
+ } catch (err) {
1143
+ console.error('[WARN] Failed to sync codex skills:', err.message);
1144
+ }
1145
+ }
1146
+
1138
1147
  codexProcess = pty.spawn(shell, shellArgs, {
1139
1148
  name: 'xterm-256color',
1140
1149
  cols: termCols,
@@ -42,6 +42,7 @@ export function getPublicPaths() {
42
42
  /**
43
43
  * Initialize codex home directory and config files for a user.
44
44
  * Safe to call multiple times - only creates files that don't already exist.
45
+ * Note: skillsDir must exist before calling this function (created in initUserDirectories).
45
46
  */
46
47
  export async function initCodexDirectories(userUuid) {
47
48
  validateUuid(userUuid);
@@ -73,6 +74,56 @@ base_url = "${baseUrl}"
73
74
  await fs.writeFile(authJsonPath, authJson, 'utf8');
74
75
  }
75
76
 
77
+ // Sync skills symlinks into .codex/skills/:
78
+ // Mirror each enabled skill from .claude/skills/ into .codex/skills/<skill> (symlink).
79
+ // The .codex/skills/.system/ directory (Codex built-in skills) is preserved unchanged.
80
+ const codexSkillsDir = path.join(codexDir, 'skills');
81
+ await fs.mkdir(codexSkillsDir, { recursive: true });
82
+
83
+ try {
84
+ // Add symlinks for newly enabled skills
85
+ let claudeSkills = [];
86
+ try {
87
+ claudeSkills = await fs.readdir(paths.skillsDir);
88
+ } catch {
89
+ // skillsDir doesn't exist yet - nothing to sync
90
+ }
91
+
92
+ for (const skillName of claudeSkills) {
93
+ const claudeSkillPath = path.join(paths.skillsDir, skillName);
94
+ const codexSkillLink = path.join(codexSkillsDir, skillName);
95
+ try {
96
+ await fs.lstat(codexSkillLink);
97
+ // Already exists - skip
98
+ } catch {
99
+ await fs.symlink(claudeSkillPath, codexSkillLink);
100
+ console.log(`[Codex] Synced skill: ${skillName}`);
101
+ }
102
+ }
103
+
104
+ // Remove stale symlinks (skills removed from .claude/skills/)
105
+ const codexSkills = await fs.readdir(codexSkillsDir);
106
+ for (const skillName of codexSkills) {
107
+ if (skillName.startsWith('.')) continue; // preserve .system and other hidden dirs
108
+ const codexSkillLink = path.join(codexSkillsDir, skillName);
109
+ const claudeSkillPath = path.join(paths.skillsDir, skillName);
110
+ try {
111
+ await fs.access(claudeSkillPath);
112
+ } catch {
113
+ // No longer in .claude/skills/ - remove if it's a symlink
114
+ try {
115
+ const stat = await fs.lstat(codexSkillLink);
116
+ if (stat.isSymbolicLink()) {
117
+ await fs.unlink(codexSkillLink);
118
+ console.log(`[Codex] Removed stale skill symlink: ${skillName}`);
119
+ }
120
+ } catch {}
121
+ }
122
+ }
123
+ } catch (err) {
124
+ console.log(`[Codex] Skills sync warning: ${err.message}`);
125
+ }
126
+
76
127
  console.log(`[Codex] Initialized codex directories for user ${userUuid}`);
77
128
  return codexDir;
78
129
  }
@@ -88,18 +139,18 @@ export async function initUserDirectories(userUuid) {
88
139
  await fs.mkdir(paths.claudeDir, { recursive: true });
89
140
  await fs.mkdir(paths.projectsDir, { recursive: true });
90
141
 
91
- // Initialize codex home directory with config files
92
- await initCodexDirectories(userUuid);
93
-
94
142
  // Create projects directory for Claude session files
95
143
  const projectsDir = path.join(paths.claudeDir, 'projects');
96
144
  await fs.mkdir(projectsDir, { recursive: true });
97
145
 
98
- // Create skills directories
146
+ // Create skills directories (must be before initCodexDirectories to allow skills symlink)
99
147
  await fs.mkdir(paths.skillsDir, { recursive: true });
100
148
  await fs.mkdir(paths.skillsImportDir, { recursive: true });
101
149
  await fs.mkdir(paths.skillsRepoDir, { recursive: true });
102
150
 
151
+ // Initialize codex home directory with config files (after skillsDir exists for symlink)
152
+ await initCodexDirectories(userUuid);
153
+
103
154
  // Create .claude.json with hasCompletedOnboarding=true
104
155
  const claudeJsonPath = path.join(paths.claudeDir, '.claude.json');
105
156
  const claudeConfig = {