@maccesar/titools 2.0.6 → 2.2.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.
Files changed (132) hide show
  1. package/{AGENTS-TEMPLATE.md → AGENTS-VERCEL-RESEARCH.md} +16 -18
  2. package/README.md +250 -168
  3. package/agents/{ti-researcher.md → ti-pro.md} +10 -10
  4. package/bin/titools.js +16 -12
  5. package/lib/cleanup.js +95 -0
  6. package/lib/commands/agents.js +146 -70
  7. package/lib/commands/skills.js +349 -0
  8. package/lib/commands/uninstall.js +189 -154
  9. package/lib/commands/update.js +104 -21
  10. package/lib/config.js +26 -20
  11. package/lib/downloader.js +1 -1
  12. package/lib/installer.js +37 -97
  13. package/lib/platform.js +9 -5
  14. package/lib/prompts/checkboxCancel.js +264 -0
  15. package/lib/prompts/selectCancel.js +204 -0
  16. package/lib/symlink.js +19 -7
  17. package/lib/utils.js +21 -31
  18. package/package.json +10 -10
  19. package/skills/alloy-guides/SKILL.md +8 -30
  20. package/skills/alloy-guides/references/CLI_TASKS.md +14 -24
  21. package/skills/alloy-guides/references/CONCEPTS.md +5 -25
  22. package/skills/alloy-guides/references/CONTROLLERS.md +2 -19
  23. package/skills/alloy-guides/references/MODELS.md +40 -10
  24. package/skills/alloy-guides/references/PURGETSS.md +1 -11
  25. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +8 -14
  26. package/skills/alloy-guides/references/VIEWS_STYLES.md +14 -25
  27. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +2 -8
  28. package/skills/alloy-guides/references/VIEWS_XML.md +29 -40
  29. package/skills/alloy-guides/references/WIDGETS.md +1 -17
  30. package/skills/alloy-howtos/SKILL.md +1 -22
  31. package/skills/alloy-howtos/references/best_practices.md +0 -17
  32. package/skills/alloy-howtos/references/cli_reference.md +1 -23
  33. package/skills/alloy-howtos/references/config_files.md +86 -15
  34. package/skills/alloy-howtos/references/custom_tags.md +14 -13
  35. package/skills/alloy-howtos/references/debugging_troubleshooting.md +3 -22
  36. package/skills/alloy-howtos/references/samples.md +4 -14
  37. package/skills/purgetss/SKILL.md +11 -37
  38. package/skills/purgetss/references/EXAMPLES.md +106 -17
  39. package/skills/purgetss/references/animation-system.md +0 -67
  40. package/skills/purgetss/references/apply-directive.md +0 -18
  41. package/skills/purgetss/references/arbitrary-values.md +0 -25
  42. package/skills/purgetss/references/class-index.md +0 -46
  43. package/skills/purgetss/references/cli-commands.md +0 -41
  44. package/skills/purgetss/references/configurable-properties.md +0 -55
  45. package/skills/purgetss/references/custom-rules.md +0 -14
  46. package/skills/purgetss/references/customization-deep-dive.md +0 -49
  47. package/skills/purgetss/references/dynamic-component-creation.md +56 -37
  48. package/skills/purgetss/references/grid-layout.md +0 -32
  49. package/skills/purgetss/references/icon-fonts.md +0 -50
  50. package/skills/purgetss/references/installation-setup.md +61 -32
  51. package/skills/purgetss/references/migration-guide.md +127 -0
  52. package/skills/purgetss/references/opacity-modifier.md +0 -34
  53. package/skills/purgetss/references/performance-tips.md +261 -0
  54. package/skills/purgetss/references/platform-modifiers.md +0 -46
  55. package/skills/purgetss/references/smart-mappings.md +0 -12
  56. package/skills/purgetss/references/tikit-components.md +393 -0
  57. package/skills/purgetss/references/titanium-resets.md +1 -27
  58. package/skills/purgetss/references/ui-ux-design.md +467 -117
  59. package/skills/{alloy-expert → ti-expert}/SKILL.md +45 -54
  60. package/skills/ti-expert/references/alloy-builtins.md +387 -0
  61. package/skills/{alloy-expert → ti-expert}/references/alloy-structure.md +147 -40
  62. package/skills/ti-expert/references/anti-patterns.md +90 -0
  63. package/skills/ti-expert/references/cli-expert.md +109 -0
  64. package/skills/{alloy-expert → ti-expert}/references/code-conventions.md +182 -49
  65. package/skills/{alloy-expert → ti-expert}/references/controller-patterns.md +103 -93
  66. package/skills/{alloy-expert → ti-expert}/references/error-handling.md +12 -12
  67. package/skills/{alloy-expert → ti-expert}/references/examples.md +144 -85
  68. package/skills/{alloy-expert → ti-expert}/references/migration-patterns.md +38 -36
  69. package/skills/{alloy-expert → ti-expert}/references/patterns.md +8 -8
  70. package/skills/ti-expert/references/performance-listview.md +251 -0
  71. package/skills/{alloy-expert/references/performance-patterns.md → ti-expert/references/performance-optimization.md} +67 -283
  72. package/skills/{alloy-expert/references/security-patterns.md → ti-expert/references/security-device.md} +12 -295
  73. package/skills/ti-expert/references/security-fundamentals.md +284 -0
  74. package/skills/{alloy-expert → ti-expert}/references/state-management.md +10 -10
  75. package/skills/ti-expert/references/testing-e2e-ci.md +432 -0
  76. package/skills/ti-expert/references/testing-unit.md +433 -0
  77. package/skills/ti-expert/references/theming.md +394 -0
  78. package/skills/ti-guides/SKILL.md +1 -14
  79. package/skills/ti-guides/references/advanced-data-and-images.md +126 -10
  80. package/skills/ti-guides/references/app-distribution.md +48 -10
  81. package/skills/ti-guides/references/application-frameworks.md +9 -2
  82. package/skills/ti-guides/references/cli-reference.md +338 -82
  83. package/skills/ti-guides/references/coding-best-practices.md +14 -4
  84. package/skills/ti-guides/references/commonjs-advanced.md +103 -6
  85. package/skills/ti-guides/references/hello-world.md +5 -2
  86. package/skills/ti-guides/references/hyperloop-native-access.md +439 -46
  87. package/skills/ti-guides/references/javascript-primer.md +4 -3
  88. package/skills/ti-guides/references/resources.md +0 -1
  89. package/skills/ti-guides/references/style-and-conventions.md +57 -9
  90. package/skills/ti-guides/references/tiapp-config.md +48 -6
  91. package/skills/ti-howtos/SKILL.md +2 -27
  92. package/skills/ti-howtos/references/android-platform-deep-dives.md +41 -90
  93. package/skills/ti-howtos/references/automation-fastlane-appium.md +15 -14
  94. package/skills/ti-howtos/references/buffer-codec-streams.md +22 -22
  95. package/skills/ti-howtos/references/cross-platform-development.md +68 -37
  96. package/skills/ti-howtos/references/debugging-profiling.md +8 -53
  97. package/skills/ti-howtos/references/extending-titanium.md +12 -51
  98. package/skills/ti-howtos/references/google-maps-v2.md +30 -29
  99. package/skills/ti-howtos/references/ios-map-kit.md +19 -25
  100. package/skills/ti-howtos/references/ios-platform-deep-dives.md +95 -283
  101. package/skills/ti-howtos/references/local-data-sources.md +45 -37
  102. package/skills/ti-howtos/references/location-and-maps.md +47 -28
  103. package/skills/ti-howtos/references/media-apis.md +84 -27
  104. package/skills/ti-howtos/references/notification-services.md +40 -89
  105. package/skills/ti-howtos/references/remote-data-sources.md +21 -36
  106. package/skills/ti-howtos/references/tutorials.md +79 -39
  107. package/skills/ti-howtos/references/using-modules.md +4 -30
  108. package/skills/ti-howtos/references/web-content-integration.md +28 -45
  109. package/skills/ti-howtos/references/webpack-build-pipeline.md +54 -18
  110. package/skills/ti-ui/SKILL.md +1 -21
  111. package/skills/ti-ui/references/accessibility-deep-dive.md +71 -25
  112. package/skills/ti-ui/references/animation-and-matrices.md +0 -47
  113. package/skills/ti-ui/references/application-structures.md +0 -46
  114. package/skills/ti-ui/references/custom-fonts-styling.md +9 -57
  115. package/skills/ti-ui/references/event-handling.md +32 -40
  116. package/skills/ti-ui/references/gestures.md +0 -40
  117. package/skills/ti-ui/references/icons-and-splash-screens.md +21 -44
  118. package/skills/ti-ui/references/layouts-and-positioning.md +47 -39
  119. package/skills/ti-ui/references/listviews-and-performance.md +187 -73
  120. package/skills/ti-ui/references/orientation.md +57 -71
  121. package/skills/ti-ui/references/platform-ui-android.md +114 -48
  122. package/skills/ti-ui/references/platform-ui-ios.md +63 -38
  123. package/skills/ti-ui/references/scrolling-views.md +14 -23
  124. package/skills/ti-ui/references/tableviews.md +6 -56
  125. package/lib/commands/install.js +0 -188
  126. package/skills/alloy-expert/references/anti-patterns.md +0 -133
  127. package/skills/alloy-expert/references/testing.md +0 -872
  128. package/skills/ti-guides/references/alloy-cli-advanced.md +0 -84
  129. package/skills/ti-guides/references/alloy-data-mastery.md +0 -29
  130. package/skills/ti-guides/references/alloy-widgets-and-themes.md +0 -19
  131. /package/skills/{alloy-expert → ti-expert}/assets/ControllerAutoCleanup.js +0 -0
  132. /package/skills/{alloy-expert → ti-expert}/references/contracts.md +0 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Skills command
3
+ * Installs and manages skills in AI coding assistant directories
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import {
9
+ SKILLS,
10
+ getPlatforms,
11
+ getAgentsSkillsDir,
12
+ } from '../config.js';
13
+ import select from '../prompts/selectCancel.js';
14
+ import checkbox, { Separator } from '../prompts/checkboxCancel.js';
15
+ import {
16
+ detectPlatforms,
17
+ detectOS,
18
+ } from '../platform.js';
19
+ import {
20
+ getSkillList,
21
+ removeSkillSymlinks,
22
+ removeLegacySkillSymlinks,
23
+ removeSkills,
24
+ removeAgents,
25
+ } from '../cleanup.js';
26
+ import {
27
+ installSkills,
28
+ installAgents,
29
+ getLocalRepoDir,
30
+ } from '../installer.js';
31
+ import { agentsCommand } from './agents.js';
32
+ import { downloadRepoArchive } from '../downloader.js';
33
+ import { createSkillSymlinks } from '../symlink.js';
34
+ import { formatList, isTitaniumProject } from '../utils.js';
35
+ import { mkdtemp } from 'fs/promises';
36
+ import { existsSync } from 'fs';
37
+ import { join, resolve } from 'path';
38
+ import { tmpdir } from 'os';
39
+
40
+ function hasAnySkillSymlink(platformSkillsDir) {
41
+ if (!platformSkillsDir || !existsSync(platformSkillsDir)) return false;
42
+ const skillList = getSkillList();
43
+ return skillList.some((skill) => existsSync(join(platformSkillsDir, skill)));
44
+ }
45
+
46
+ /**
47
+ * Skills command handler
48
+ * @param {Object} options - Command options
49
+ */
50
+ export async function skillsCommand(options) {
51
+ console.log('');
52
+ console.log(chalk.bold.blue('Titanium SDK Skills Manager'));
53
+ console.log('');
54
+
55
+ let isLocal = options.local;
56
+ let customPath = options.path;
57
+
58
+ // Auto-detection logic: If no explicit mode, check if we are in a Titanium project
59
+ if (!isLocal && !customPath) {
60
+ const projectDir = process.cwd();
61
+ const isProject = isTitaniumProject(projectDir);
62
+
63
+ if (isProject) {
64
+ try {
65
+ const mode = await select({
66
+ message: 'Titanium project detected. Where do you want to install the skills:',
67
+ choices: [
68
+ { name: 'Global (user home) - Recommended for personal use', value: 'global' },
69
+ { name: 'Local (current project) - Best for shared repositories', value: 'local' },
70
+ ],
71
+ theme: {
72
+ style: {
73
+ answer: () => '',
74
+ prefix: () => chalk.cyan('?'),
75
+ },
76
+ },
77
+ });
78
+ if (mode === 'cancel') {
79
+ console.log('Cancelled.');
80
+ process.exit(0);
81
+ }
82
+ isLocal = mode === 'local';
83
+ } catch (error) {
84
+ console.log('\nCancelled.');
85
+ process.exit(0);
86
+ }
87
+ }
88
+ }
89
+
90
+ // Determine base directory (Local vs Global)
91
+ const baseDir = isLocal ? process.cwd() : (customPath ? resolve(customPath) : undefined);
92
+
93
+ if (isLocal) {
94
+ console.log(chalk.cyan('Mode: Local installation (current project)'));
95
+ } else if (customPath) {
96
+ console.log(chalk.cyan(`Mode: Custom path (${baseDir})`));
97
+ } else {
98
+ console.log(chalk.cyan('Mode: Global installation (user home)'));
99
+ }
100
+ console.log('');
101
+
102
+ // Detect installed platforms at the target base directory
103
+ const localPlatforms = detectPlatforms(baseDir);
104
+ const globalPlatforms = detectPlatforms();
105
+ let detectedPlatforms = localPlatforms;
106
+
107
+ if (isLocal || options.path) {
108
+ const localNames = new Set(localPlatforms.map((platform) => platform.name));
109
+ const globalNames = new Set(globalPlatforms.map((platform) => platform.name));
110
+ const allPlatforms = getPlatforms(baseDir);
111
+ const merged = [...localPlatforms];
112
+ for (const platform of allPlatforms) {
113
+ if (globalNames.has(platform.name) && !localNames.has(platform.name)) {
114
+ merged.push(platform);
115
+ }
116
+ }
117
+ detectedPlatforms = merged;
118
+ }
119
+
120
+ if (detectedPlatforms.length === 0 && !options.path && !isLocal) {
121
+ console.log(chalk.yellow('No AI coding assistants detected globally.'));
122
+ console.log('Install one of: Claude Code, Gemini CLI, or Codex CLI');
123
+ console.log('Or use: titools install --local');
124
+ process.exit(1);
125
+ }
126
+
127
+ // Show detected platforms
128
+ if (detectedPlatforms.length > 0) {
129
+ for (const platform of detectedPlatforms) {
130
+ console.log(chalk.green('✓'), `${platform.displayName} detected`);
131
+ }
132
+ console.log('');
133
+ } else if (isLocal) {
134
+ // If local and none detected, still allow selection
135
+ detectedPlatforms = getPlatforms(baseDir);
136
+ }
137
+
138
+ // Select platforms to install
139
+ let selectedPlatforms = [];
140
+
141
+ if (options.path) {
142
+ // Custom path mode - skip platform selection
143
+ selectedPlatforms = detectedPlatforms;
144
+ } else if (options.all) {
145
+ // Install to all detected platforms
146
+ selectedPlatforms = detectedPlatforms;
147
+ } else {
148
+ // Modern functional selection
149
+ try {
150
+ const platformChoices = await checkbox({
151
+ message: 'Select platforms to sync:',
152
+ choices: [
153
+ ...detectedPlatforms.map((p) => ({
154
+ name: p.displayName,
155
+ value: p.name,
156
+ checked: hasAnySkillSymlink(p.skillsDir),
157
+ })),
158
+ new Separator(' ')
159
+ ],
160
+ shortcuts: { invert: null },
161
+ theme: {
162
+ style: {
163
+ renderSelectedChoices: () => '',
164
+ prefix: () => chalk.cyan('?'),
165
+ },
166
+ },
167
+ });
168
+
169
+ if (platformChoices.includes('cancel')) {
170
+ console.log('Cancelled.');
171
+ process.exit(0);
172
+ }
173
+
174
+ selectedPlatforms = detectedPlatforms.filter((p) =>
175
+ platformChoices.includes(p.name)
176
+ );
177
+ } catch (error) {
178
+ console.log('\nCancelled.');
179
+ process.exit(0);
180
+ }
181
+ }
182
+
183
+ const removeOnly = selectedPlatforms.length === 0;
184
+ if (removeOnly) {
185
+ console.log(chalk.yellow('No platforms selected. Removing all platform symlinks and agents.'));
186
+ }
187
+ const showGeminiWarning = (() => {
188
+ if (removeOnly || !isLocal) return false;
189
+ const globalGemini = globalPlatforms.find((platform) => platform.name === 'gemini');
190
+ const geminiSelected = selectedPlatforms.some((platform) => platform.name === 'gemini');
191
+ return !!(geminiSelected && globalGemini && hasAnySkillSymlink(globalGemini.skillsDir));
192
+ })();
193
+
194
+ // Get repository directory (local or download)
195
+ const spinner = ora();
196
+ let repoDir = null;
197
+ let tempDir = null;
198
+
199
+ if (!removeOnly) {
200
+ repoDir = getLocalRepoDir();
201
+
202
+ if (!repoDir) {
203
+ // Download from GitHub
204
+ spinner.start('Downloading from GitHub...');
205
+ try {
206
+ tempDir = await mkdtemp(join(tmpdir(), 'titanium-skills-'));
207
+ repoDir = await downloadRepoArchive(tempDir);
208
+ spinner.succeed('Downloaded from GitHub');
209
+ } catch (error) {
210
+ spinner.fail('Failed to download');
211
+ console.error(chalk.red(error.message));
212
+ process.exit(1);
213
+ }
214
+ } else {
215
+ console.log(chalk.green('Using local repository'));
216
+ }
217
+ }
218
+
219
+ const selectedPlatformNames = new Set(selectedPlatforms.map((platform) => platform.name));
220
+ const removedPlatformNames = [];
221
+ const claudeSelected = selectedPlatformNames.has('claude');
222
+
223
+ try {
224
+ if (!removeOnly) {
225
+ // Install skills
226
+ spinner.start('Installing skills...');
227
+ const skillsResult = await installSkills(repoDir, baseDir);
228
+ spinner.succeed(`${SKILLS.length} skills installed`);
229
+
230
+ if (claudeSelected) {
231
+ // Install agents
232
+ spinner.start('Installing agents...');
233
+ const agentsResult = await installAgents(repoDir, baseDir);
234
+ if (agentsResult.installed.length > 0) {
235
+ spinner.succeed('Platform agents installed');
236
+ } else {
237
+ spinner.info('No agents to install (Claude Code not detected)');
238
+ }
239
+ } else {
240
+ spinner.start('Removing agents...');
241
+ const agentsResult = removeAgents(baseDir);
242
+ if (agentsResult.removed.length > 0) {
243
+ spinner.succeed('Platform agents removed');
244
+ } else {
245
+ spinner.info('No agents to remove');
246
+ }
247
+ }
248
+
249
+ // Create symlinks for selected platforms
250
+ for (const platform of selectedPlatforms) {
251
+ removeLegacySkillSymlinks(platform.skillsDir);
252
+ spinner.start(`Linking ${platform.displayName}...`);
253
+ const symlinkResult = await createSkillSymlinks(
254
+ platform.skillsDir,
255
+ SKILLS,
256
+ baseDir
257
+ );
258
+ if (symlinkResult.linked.length === SKILLS.length) {
259
+ spinner.succeed(`${platform.displayName}: Skills linked`);
260
+ } else {
261
+ spinner.warn(
262
+ `${platform.displayName}: ${symlinkResult.linked.length}/${SKILLS.length} skills linked`
263
+ );
264
+ }
265
+ }
266
+ } else {
267
+ const agentsResult = removeAgents(baseDir);
268
+ const skillsResult = removeSkills(baseDir);
269
+ const platformResults = detectedPlatforms.map((platform) => {
270
+ const symlinkResult = removeSkillSymlinks(platform.skillsDir);
271
+ return {
272
+ displayName: platform.displayName,
273
+ removedCount: symlinkResult.removed.length,
274
+ };
275
+ });
276
+ const anyRemoved = platformResults.some((result) => result.removedCount > 0);
277
+
278
+ if (skillsResult.removed.length > 0) {
279
+ console.log(chalk.green('✓'), `${skillsResult.removed.length} skills removed`);
280
+ } else {
281
+ console.log(chalk.gray('ℹ'), 'No skills to remove');
282
+ }
283
+
284
+ if (agentsResult.removed.length > 0) {
285
+ console.log(chalk.green('✓'), 'Platform agents removed');
286
+ } else {
287
+ console.log(chalk.gray('ℹ'), 'No agents to remove');
288
+ }
289
+
290
+ for (const result of platformResults) {
291
+ if (result.removedCount > 0) {
292
+ console.log(chalk.green('✓'), `${result.displayName}: Skills unlinked`);
293
+ } else {
294
+ console.log(chalk.gray('ℹ'), `${result.displayName}: No symlinks found`);
295
+ }
296
+ }
297
+ }
298
+
299
+ // Remove symlinks for unselected detected platforms
300
+ if (!removeOnly) {
301
+ for (const platform of detectedPlatforms) {
302
+ if (selectedPlatformNames.has(platform.name)) continue;
303
+ spinner.start(`Unlinking ${platform.displayName}...`);
304
+ const symlinkResult = removeSkillSymlinks(platform.skillsDir);
305
+ if (symlinkResult.removed.length > 0) {
306
+ spinner.succeed(`${platform.displayName}: Skills unlinked`);
307
+ removedPlatformNames.push(platform.displayName);
308
+ } else {
309
+ spinner.info(`${platform.displayName}: No symlinks found`);
310
+ }
311
+ }
312
+ }
313
+
314
+ // Summary
315
+ console.log('');
316
+ console.log(chalk.green('✓ skills sync complete!'));
317
+ if (showGeminiWarning) {
318
+ console.log('');
319
+ console.log(chalk.yellow('⚠ Gemini CLI: local skills will override global skills for this project.'));
320
+ }
321
+ console.log('');
322
+ const isProjectDir = isTitaniumProject(process.cwd());
323
+ if (!removeOnly && selectedPlatforms.length > 0 && isProjectDir) {
324
+ if (process.stdin.isTTY) {
325
+ await agentsCommand(process.cwd(), { inline: true });
326
+ } else {
327
+ console.log(
328
+ chalk.bold('▸'),
329
+ 'Sync knowledge index files (AGENTS.md/CLAUDE.md/GEMINI.md):',
330
+ chalk.cyan('titools sync')
331
+ );
332
+ console.log('');
333
+ }
334
+ }
335
+
336
+ if (!removeOnly && !isLocal && detectOS() === 'windows') {
337
+ console.log(chalk.yellow('▸'), 'Windows: Ensure ~/bin is in your PATH');
338
+ console.log('');
339
+ }
340
+
341
+ } finally {
342
+ // Clean up temp directory if we downloaded
343
+ if (tempDir) {
344
+ await import('fs-extra').then(({ remove }) => remove(tempDir));
345
+ }
346
+ }
347
+ }
348
+
349
+ export default skillsCommand;