@maccesar/titools 2.2.9 → 2.2.10

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.
@@ -42,6 +42,49 @@ function hasAnySkillSymlink(platformSkillsDir) {
42
42
  return skillList.some((skill) => existsSync(join(platformSkillsDir, skill)));
43
43
  }
44
44
 
45
+ /**
46
+ * Perform the actual update for a specific scope
47
+ * @param {string|undefined} baseDir - Base directory (undefined = global, path = local)
48
+ * @param {string} repoDir - Repository directory
49
+ * @param {Object} spinner - Ora spinner instance
50
+ * @returns {Promise<void>}
51
+ */
52
+ async function performUpdate(baseDir, repoDir, spinner) {
53
+ // Detect platforms with existing symlinks (only update those)
54
+ const detectedPlatforms = detectPlatforms(baseDir);
55
+ const platformsWithSymlinks = detectedPlatforms.filter((p) =>
56
+ hasAnySkillSymlink(p.skillsDir)
57
+ );
58
+
59
+ // Install skills
60
+ spinner.start('Syncing skills...');
61
+ const skillsResult = await installSkills(repoDir, baseDir);
62
+ spinner.succeed(`${skillsResult.installed.length} skills updated`);
63
+
64
+ // Install agents (only if Claude Code has symlinks)
65
+ const claudePlatform = platformsWithSymlinks.find((p) => p.name === 'claude');
66
+ if (claudePlatform) {
67
+ spinner.start('Syncing agents...');
68
+ const agentsResult = await installAgents(repoDir, baseDir);
69
+ if (agentsResult.installed.length > 0) {
70
+ spinner.succeed('Platform agents updated');
71
+ } else {
72
+ spinner.info('No agents to sync');
73
+ }
74
+ }
75
+
76
+ cleanupLegacyArtifacts(baseDir);
77
+
78
+ // Update symlinks silently for platforms that already had them
79
+ for (const platform of platformsWithSymlinks) {
80
+ await createSkillSymlinks(
81
+ platform.skillsDir,
82
+ SKILLS,
83
+ baseDir
84
+ );
85
+ }
86
+ }
87
+
45
88
  /**
46
89
  * Update command handler
47
90
  * @param {Object} options - Command options
@@ -65,15 +108,16 @@ export async function updateCommand(options) {
65
108
  const localPlatforms = detectPlatforms(projectDir);
66
109
  const hasLocalSkills = isProject && hasSkillsAt(projectDir);
67
110
  const hasGlobalSkills = hasSkillsAt(undefined);
68
- if (hasLocalSkills && !hasGlobalSkills) {
69
- baseDir = projectDir;
70
- } else if (isProject && localPlatforms.length > 0) {
111
+
112
+ // Only show prompt if BOTH local and global skills exist
113
+ if (hasLocalSkills && hasGlobalSkills) {
71
114
  try {
72
115
  const scope = await select({
73
- message: 'Local installation detected. What do you want to update:',
116
+ message: 'Both local and global skills detected. What do you want to update:',
74
117
  choices: [
75
118
  { name: 'Global skills (user home)', value: 'global' },
76
119
  { name: 'Local skills (current project)', value: 'local' },
120
+ { name: 'Both locations', value: 'both' },
77
121
  ],
78
122
  theme: {
79
123
  style: {
@@ -87,32 +131,44 @@ export async function updateCommand(options) {
87
131
  process.exit(0);
88
132
  }
89
133
  if (scope === 'local') {
90
- baseDir = process.cwd();
134
+ baseDir = projectDir;
135
+ } else if (scope === 'both') {
136
+ baseDir = 'both';
91
137
  }
92
138
  } catch (error) {
93
139
  console.log('\nCancelled.');
94
140
  process.exit(0);
95
141
  }
142
+ } else if (hasLocalSkills && !hasGlobalSkills) {
143
+ // Only local skills exist, update local
144
+ baseDir = projectDir;
96
145
  }
146
+ // If only global skills exist, baseDir remains undefined (global)
97
147
  }
98
148
 
99
- if (baseDir) {
149
+ // Display mode
150
+ if (baseDir === 'both') {
151
+ console.log(chalk.cyan('Mode: Updating both global and local skills'));
152
+ } else if (baseDir) {
100
153
  console.log(chalk.cyan('Mode: Local update (current project)'));
101
154
  } else {
102
155
  console.log(chalk.cyan('Mode: Global update (user home)'));
103
156
  }
104
157
  console.log('');
105
158
 
106
- const skillsDir = getAgentsSkillsDir(baseDir);
107
- const hasSkillsInstalled = skillsDir && SKILLS.some((skill) => existsSync(join(skillsDir, skill)));
108
- if (!hasSkillsInstalled) {
109
- console.log(chalk.yellow('No skills installed at this location.'));
110
- console.log('Install them first with:');
111
- console.log(' titools install');
112
- console.log('');
113
- console.log('Looked for skills in:');
114
- console.log(` ${baseDir ? 'Local' : 'Global'}: ${skillsDir}`);
115
- return;
159
+ // Verify skills are installed
160
+ if (baseDir !== 'both') {
161
+ const skillsDir = getAgentsSkillsDir(baseDir);
162
+ const hasSkillsInstalled = skillsDir && SKILLS.some((skill) => existsSync(join(skillsDir, skill)));
163
+ if (!hasSkillsInstalled) {
164
+ console.log(chalk.yellow('No skills installed at this location.'));
165
+ console.log('Install them first with:');
166
+ console.log(' titools install');
167
+ console.log('');
168
+ console.log('Looked for skills in:');
169
+ console.log(` ${baseDir ? 'Local' : 'Global'}: ${skillsDir}`);
170
+ return;
171
+ }
116
172
  }
117
173
 
118
174
  // Check for updates
@@ -121,7 +177,7 @@ export async function updateCommand(options) {
121
177
  try {
122
178
  const hasUpdate = await checkForUpdate(PACKAGE_VERSION);
123
179
 
124
- // If there's a newer version on GitHub, prompt user to update CLI first
180
+ // If there's a newer version on npm, prompt user to update CLI first
125
181
  if (hasUpdate) {
126
182
  let latestVersion = '(newer)';
127
183
  try {
@@ -158,38 +214,18 @@ export async function updateCommand(options) {
158
214
  return;
159
215
  }
160
216
 
161
- // Detect platforms with existing symlinks (only update those)
162
- const detectedPlatforms = detectPlatforms(baseDir);
163
- const platformsWithSymlinks = detectedPlatforms.filter((p) =>
164
- hasAnySkillSymlink(p.skillsDir)
165
- );
166
-
167
- // Install skills
168
- spinner.start('Syncing skills...');
169
- const skillsResult = await installSkills(repoDir, baseDir);
170
- spinner.succeed(`${skillsResult.installed.length} skills updated`);
171
-
172
- // Install agents (only if Claude Code has symlinks)
173
- const claudePlatform = platformsWithSymlinks.find((p) => p.name === 'claude');
174
- if (claudePlatform) {
175
- spinner.start('Syncing agents...');
176
- const agentsResult = await installAgents(repoDir, baseDir);
177
- if (agentsResult.installed.length > 0) {
178
- spinner.succeed('Platform agents updated');
179
- } else {
180
- spinner.info('No agents to sync');
181
- }
182
- }
183
-
184
- cleanupLegacyArtifacts(baseDir);
217
+ // Perform update(s)
218
+ if (baseDir === 'both') {
219
+ // Update both global and local
220
+ console.log(chalk.bold('Updating global skills...'));
221
+ await performUpdate(undefined, repoDir, spinner);
222
+ console.log('');
185
223
 
186
- // Update symlinks silently for platforms that already had them
187
- for (const platform of platformsWithSymlinks) {
188
- await createSkillSymlinks(
189
- platform.skillsDir,
190
- SKILLS,
191
- baseDir
192
- );
224
+ console.log(chalk.bold('Updating local skills...'));
225
+ await performUpdate(process.cwd(), repoDir, spinner);
226
+ } else {
227
+ // Update single scope
228
+ await performUpdate(baseDir, repoDir, spinner);
193
229
  }
194
230
 
195
231
  // Summary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/titools",
3
- "version": "2.2.9",
3
+ "version": "2.2.10",
4
4
  "description": "Titanium SDK skills and agents for AI coding assistants (Claude Code, Gemini CLI, Codex CLI)",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",