@maccesar/titools 3.2.0 → 4.0.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 (102) hide show
  1. package/README.md +27 -61
  2. package/lib/cleanup.js +29 -0
  3. package/lib/commands/agents.js +8 -7
  4. package/lib/commands/skills.js +118 -23
  5. package/lib/config.js +13 -15
  6. package/lib/installer.js +5 -3
  7. package/lib/utils.js +36 -0
  8. package/package.json +1 -1
  9. package/skills/alloy-guides/SKILL.md +188 -0
  10. package/skills/alloy-guides/references/CLI_TASKS.md +233 -0
  11. package/skills/alloy-guides/references/CONCEPTS.md +171 -0
  12. package/skills/alloy-guides/references/CONTROLLERS.md +279 -0
  13. package/skills/alloy-guides/references/MODELS.md +742 -0
  14. package/skills/alloy-guides/references/MODELS_ADVANCED.md +482 -0
  15. package/skills/alloy-guides/references/PURGETSS.md +57 -0
  16. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +235 -0
  17. package/skills/alloy-guides/references/VIEWS_STYLES.md +375 -0
  18. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +102 -0
  19. package/skills/alloy-guides/references/VIEWS_XML.md +581 -0
  20. package/skills/alloy-guides/references/WIDGETS.md +160 -0
  21. package/skills/alloy-howtos/SKILL.md +179 -0
  22. package/skills/alloy-howtos/references/best_practices.md +121 -0
  23. package/skills/alloy-howtos/references/cli_reference.md +230 -0
  24. package/skills/alloy-howtos/references/config_files.md +158 -0
  25. package/skills/alloy-howtos/references/custom_tags.md +148 -0
  26. package/skills/alloy-howtos/references/debugging_troubleshooting.md +78 -0
  27. package/skills/alloy-howtos/references/samples.md +156 -0
  28. package/skills/purgetss/SKILL.md +184 -181
  29. package/skills/purgetss/references/EXAMPLES.md +8 -6
  30. package/skills/purgetss/references/app-branding.md +5 -0
  31. package/skills/purgetss/references/apply-directive.md +1 -1
  32. package/skills/purgetss/references/class-categories.md +1 -1
  33. package/skills/purgetss/references/class-index-properties.md +618 -0
  34. package/skills/purgetss/references/class-index.md +4 -610
  35. package/skills/purgetss/references/cli-commands.md +41 -410
  36. package/skills/purgetss/references/custom-fonts.md +292 -0
  37. package/skills/purgetss/references/custom-rules.md +1 -1
  38. package/skills/purgetss/references/customization-deep-dive.md +64 -1
  39. package/skills/purgetss/references/icon-fonts.md +208 -118
  40. package/skills/purgetss/references/installation-setup.md +4 -4
  41. package/skills/purgetss/references/migration-guide.md +53 -1
  42. package/skills/purgetss/references/multi-density-images.md +53 -2
  43. package/skills/purgetss/references/svg-pipeline.md +249 -0
  44. package/skills/purgetss/references/tikit-components.md +2 -2
  45. package/skills/purgetss/references/titanium-resets.md +2 -2
  46. package/skills/purgetss/references/ui-ux-design.md +7 -5
  47. package/skills/purgetss/references/version-history.md +71 -82
  48. package/skills/ti-api/SKILL.md +128 -0
  49. package/skills/ti-api/references/api-android.md +675 -0
  50. package/skills/ti-api/references/api-app-platform.md +636 -0
  51. package/skills/ti-api/references/api-core.md +764 -0
  52. package/skills/ti-api/references/api-data-network.md +641 -0
  53. package/skills/ti-api/references/api-media.md +655 -0
  54. package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
  55. package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
  56. package/skills/ti-api/references/api-modules-map.md +632 -0
  57. package/skills/ti-api/references/api-modules-nfc.md +725 -0
  58. package/skills/ti-api/references/api-modules-social-misc.md +526 -0
  59. package/skills/ti-api/references/api-services.md +700 -0
  60. package/skills/ti-api/references/api-ui-android.md +499 -0
  61. package/skills/ti-api/references/api-ui-extras.md +702 -0
  62. package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
  63. package/skills/ti-api/references/api-ui-ios.md +756 -0
  64. package/skills/ti-api/references/api-ui-lists.md +581 -0
  65. package/skills/ti-api/references/api-ui-text-input.md +607 -0
  66. package/skills/ti-api/references/api-ui-views.md +572 -0
  67. package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
  68. package/skills/ti-api/references/api-xml-global.md +743 -0
  69. package/skills/ti-guides/SKILL.md +82 -0
  70. package/skills/ti-guides/references/advanced-data-and-images.md +155 -0
  71. package/skills/ti-guides/references/android-manifest.md +97 -0
  72. package/skills/ti-guides/references/app-distribution.md +373 -0
  73. package/skills/ti-guides/references/application-frameworks.md +366 -0
  74. package/skills/ti-guides/references/cli-reference.md +700 -0
  75. package/skills/ti-guides/references/coding-best-practices.md +150 -0
  76. package/skills/ti-guides/references/commonjs-advanced.md +279 -0
  77. package/skills/ti-guides/references/hello-world.md +99 -0
  78. package/skills/ti-guides/references/hyperloop-native-access.md +458 -0
  79. package/skills/ti-guides/references/javascript-primer.md +402 -0
  80. package/skills/ti-guides/references/reserved-words.md +36 -0
  81. package/skills/ti-guides/references/resources.md +172 -0
  82. package/skills/ti-guides/references/style-and-conventions.md +104 -0
  83. package/skills/ti-guides/references/tiapp-config.md +655 -0
  84. package/skills/ti-howtos/SKILL.md +131 -0
  85. package/skills/ti-howtos/references/android-platform-deep-dives.md +609 -0
  86. package/skills/ti-howtos/references/automation-fastlane-appium.md +96 -0
  87. package/skills/ti-howtos/references/buffer-codec-streams.md +162 -0
  88. package/skills/ti-howtos/references/cross-platform-development.md +358 -0
  89. package/skills/ti-howtos/references/debugging-profiling.md +473 -0
  90. package/skills/ti-howtos/references/extending-titanium.md +684 -0
  91. package/skills/ti-howtos/references/google-maps-v2.md +172 -0
  92. package/skills/ti-howtos/references/ios-map-kit.md +149 -0
  93. package/skills/ti-howtos/references/ios-platform-deep-dives.md +595 -0
  94. package/skills/ti-howtos/references/local-data-sources.md +310 -0
  95. package/skills/ti-howtos/references/location-and-maps.md +267 -0
  96. package/skills/ti-howtos/references/media-apis.md +268 -0
  97. package/skills/ti-howtos/references/notification-services.md +539 -0
  98. package/skills/ti-howtos/references/remote-data-sources.md +339 -0
  99. package/skills/ti-howtos/references/tutorials.md +552 -0
  100. package/skills/ti-howtos/references/using-modules.md +182 -0
  101. package/skills/ti-howtos/references/web-content-integration.md +288 -0
  102. package/skills/ti-howtos/references/webpack-build-pipeline.md +125 -0
package/README.md CHANGED
@@ -8,14 +8,12 @@
8
8
 
9
9
  </div>
10
10
 
11
- TiTools is a Titanium SDK toolkit for AI coding assistants. It ships 3 opinionated skills (`ti-expert`, `purgetss`, `ti-ui`), a research agent, and reference files covering Titanium architecture, PurgeTSS styling, and UI/UX patterns.
11
+ TiTools is a Titanium SDK toolkit for AI coding assistants. It ships 8 skills — 3 opinionated (`ti-expert`, `purgetss`, `ti-ui`) plus 5 documentation-mirror skills (`ti-api`, `ti-guides`, `ti-howtos`, `alloy-guides`, `alloy-howtos`) — a research agent, and reference files covering Titanium architecture, API reference, native how-tos, Alloy MVC, PurgeTSS styling, and UI/UX patterns.
12
12
 
13
13
  The reference files are maintained against official documentation whenever an official source exists, so the assistant can retrieve current framework behavior instead of guessing from generic training data.
14
14
 
15
15
  Without TiTools, assistants rely on general training data. That data can be outdated or too generic for Titanium work. With TiTools, the assistant can look up Alloy architecture, memory cleanup patterns, PurgeTSS utility classes, and platform-specific APIs.
16
16
 
17
- > **v3.0.0 — doc-based skills moved to `tidev/skills`.** Five documentation-only skills (`ti-api`, `ti-guides`, `ti-howtos`, `alloy-guides`, `alloy-howtos`) are now maintained in the official [`tidev/skills`](https://github.com/tidev/skills) repository alongside the rest of the TiDev community tooling. TiTools now focuses on the three opinionated skills above. See [Doc-based skills moved to tidev/skills](#doc-based-skills-moved-to-tidevskills) below for upgrade notes.
18
-
19
17
  Vercel's AGENTS.md evaluation reports a 100% pass rate for the knowledge index approach, compared to 53-79% using skills alone.
20
18
 
21
19
  ---
@@ -74,9 +72,9 @@ claude # or gemini, or codex
74
72
  ```
75
73
 
76
74
  What gets installed:
77
- - The 3 TiTools skills to `~/.agents/skills/`
75
+ - The 8 TiTools skills to `~/.agents/skills/`
78
76
  - ti-pro agent for Claude Code
79
- - Platform symlinks for Claude Code and Gemini CLI (Codex CLI auto-discovers from `~/.agents/skills/` — no symlink needed)
77
+ - Platform symlinks for Claude Code (Gemini CLI and Codex CLI auto-discover from `~/.agents/skills/` — no symlinks needed)
80
78
  - Knowledge index in your project's `AGENTS.md`/`CLAUDE.md`/`GEMINI.md`
81
79
  - Claude Code SessionStart hook for auto-updates
82
80
 
@@ -101,47 +99,15 @@ Use **Option A** if you only use Claude Code. Use **Option B** if you use multip
101
99
 
102
100
  ---
103
101
 
104
- ## Doc-based skills moved to tidev/skills
105
-
106
- Starting with **v3.0.0**, five documentation-only skills migrated to the official [`tidev/skills`](https://github.com/tidev/skills) repository:
107
-
108
- - `ti-api` — Complete Titanium API reference
109
- - `ti-guides` — SDK fundamentals (Hyperloop, distribution, tiapp.xml)
110
- - `ti-howtos` — Native feature integration (location, push, media, platform-specific APIs)
111
- - `alloy-guides` — Alloy MVC framework reference
112
- - `alloy-howtos` — Alloy CLI, configuration, debugging
113
-
114
- **Why the move?** These skills are pure documentation mirrors of official Titanium and Alloy docs, so they belong in the community-maintained TiDev repository alongside the SDK source itself. Hansemann ([@hansemannn](https://github.com/hansemannn)) — TiDev maintainer — accepted the first batch in [tidev/skills#1](https://github.com/tidev/skills/pull/1) and `ti-howtos` followed in [tidev/skills#2](https://github.com/tidev/skills/pull/2).
115
-
116
- **What stays in TiTools:** the three *opinionated* skills that reflect personal Titanium conventions and toolchain choices (PurgeTSS-first styling, specific architecture patterns).
117
-
118
- ### Upgrading from v2.x
119
-
120
- If you have v2.x installed, the five migrated skills will be **auto-removed** the next time you run `titools update` (they're listed in `LEGACY_SKILLS`). To get them back, install from `tidev/skills` directly:
121
-
122
- ```bash
123
- git clone https://github.com/tidev/skills.git ~/Developer/git-clones/tidev-skills
124
- # Then symlink the five skills into ~/.agents/skills/ or your AI assistant's skills dir
125
- ln -s ~/Developer/git-clones/tidev-skills/skills/ti-api ~/.agents/skills/ti-api
126
- ln -s ~/Developer/git-clones/tidev-skills/skills/ti-guides ~/.agents/skills/ti-guides
127
- ln -s ~/Developer/git-clones/tidev-skills/skills/ti-howtos ~/.agents/skills/ti-howtos
128
- ln -s ~/Developer/git-clones/tidev-skills/skills/alloy-guides ~/.agents/skills/alloy-guides
129
- ln -s ~/Developer/git-clones/tidev-skills/skills/alloy-howtos ~/.agents/skills/alloy-howtos
130
- ```
131
-
132
- Refer to the [`tidev/skills` README](https://github.com/tidev/skills#readme) for the canonical install instructions, which may evolve independently of TiTools.
133
-
134
- ---
135
-
136
102
  ## Compatible platforms
137
103
 
138
- | Platform | Status | Installation Path |
139
- | --------------------------------------------------------- | ---------------- | ------------------- |
140
- | [Claude Code](https://claude.ai/claude-code) | Fully Compatible | `~/.claude/skills/` |
141
- | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Fully Compatible | `~/.gemini/skills/` |
104
+ | Platform | Status | Skills Path |
105
+ | --------------------------------------------------------- | ---------------- | ------------------------------------- |
106
+ | [Claude Code](https://claude.ai/claude-code) | Fully Compatible | `~/.claude/skills/` (symlinked) |
107
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Fully Compatible | `~/.agents/skills/` (auto-discovered) |
142
108
  | [Codex CLI](https://developers.openai.com/codex/cli/) | Fully Compatible | `~/.agents/skills/` (auto-discovered) |
143
109
 
144
- All three platforms use the Agent Skills open standard: a `SKILL.md` file with YAML frontmatter.
110
+ All three platforms use the Agent Skills open standard: a `SKILL.md` file with YAML frontmatter. Gemini CLI and Codex CLI auto-discover skills from `~/.agents/skills/` per [agentskills.io](https://agentskills.io), so TiTools does not create redundant platform-specific symlinks for them.
145
111
 
146
112
  ---
147
113
 
@@ -194,7 +160,7 @@ Together they work like this:
194
160
  | ------------------------------- | ------------------------------- | ----------------------------- |
195
161
  | "Create a login screen" | Context about project structure | `ti-expert`, `ti-ui` |
196
162
  | "Optimize ListView performance" | Points to docs location | `ti-ui` reads specific files |
197
- | "Implement push notifications" | API reference paths | `ti-howtos` (via tidev/skills) |
163
+ | "Implement push notifications" | API reference paths | `ti-howtos` |
198
164
 
199
165
  You do not need to explicitly invoke skills. The AI detects when to use them based on your question.
200
166
 
@@ -206,16 +172,21 @@ The knowledge index is based on the latest Titanium SDK documentation. If your p
206
172
 
207
173
  ## Skills overview
208
174
 
209
- | Skill | Purpose | Best For |
210
- | --------- | ------------------------------- | --------------------------------------- |
211
- | ti-expert | Architecture and implementation | Starting point for most tasks |
212
- | purgetss | Utility-first styling | UI styling and animations |
213
- | ti-ui | UI/UX patterns | Complex layouts, ListViews, platform UI |
175
+ | Skill | Purpose | Best For |
176
+ | ------------ | ----------------------------------- | --------------------------------------- |
177
+ | ti-expert | Architecture and implementation | Starting point for most tasks |
178
+ | purgetss | Utility-first styling | UI styling and animations |
179
+ | ti-ui | UI/UX patterns | Complex layouts, ListViews, platform UI |
180
+ | ti-api | Complete Titanium API reference | Looking up properties, methods, events |
181
+ | ti-guides | SDK fundamentals | tiapp.xml, Hyperloop, distribution |
182
+ | ti-howtos | Native feature integration | Location, push, media, platform APIs |
183
+ | alloy-guides | Alloy MVC framework reference | Controllers, models, views, widgets |
184
+ | alloy-howtos | Alloy CLI, configuration, debugging | alloy.jmk, config.json, custom XML tags |
214
185
 
215
186
  Notes:
216
- - `purgetss` reference files are audited against the official PurgeTSS documentation, but the workflow conventions are opinionated.
217
- - `ti-expert` and `ti-ui` reflect personal Titanium conventions (biased toward PurgeTSS, Alloy-first).
218
- - For purely doc-based references (API surface, native-feature how-tos, Alloy MVC, SDK guides, Alloy CLI), install [`tidev/skills`](https://github.com/tidev/skills) — it pairs cleanly with TiTools.
187
+ - The first three (`ti-expert`, `purgetss`, `ti-ui`) are opinionated workflow skills reflecting TiTools' Titanium conventions.
188
+ - The latter five are documentation-mirror skills sourced from the official Titanium SDK docs (`tidev/titanium-docs`) and audited against them via the internal `titools-skill-auditor`.
189
+ - `purgetss` reference files are audited against the official PurgeTSS documentation, but its workflow conventions are opinionated.
219
190
 
220
191
  ---
221
192
 
@@ -441,10 +412,6 @@ Key rules:
441
412
 
442
413
  ---
443
414
 
444
- > **Looking for `ti-api`, `ti-guides`, `ti-howtos`, `alloy-guides`, or `alloy-howtos`?** Those five documentation-only skills now live in [`tidev/skills`](https://github.com/tidev/skills). See [Doc-based skills moved to tidev/skills](#doc-based-skills-moved-to-tidevskills) for install instructions.
445
-
446
- ---
447
-
448
415
  ## Usage examples and best practices
449
416
 
450
417
  ### Example prompts
@@ -540,13 +507,12 @@ Behavior depends on where you run it:
540
507
  | With `--local` flag | Installs skills locally to `./.agents/skills/` in the current project |
541
508
 
542
509
  What it does:
543
- - Installs the 3 TiTools skills (global or local depending on context)
510
+ - Installs the 8 TiTools skills (global or local depending on context)
544
511
  - Installs ti-pro agent for Claude Code
545
- - Detects installed AI platforms and lets you choose which to link
546
- - Creates symlinks from platform directories to central skills
547
- - Cleans up legacy artifacts (`alloy-expert` skill, `ti-researcher` agent)
512
+ - Detects installed AI platforms and lets you choose which to link (only Claude Code needs platform symlinks; Gemini CLI and Codex CLI auto-discover from `~/.agents/skills/`)
513
+ - Creates symlinks from `~/.claude/skills/` to the central `~/.agents/skills/`
514
+ - Cleans up legacy artifacts (`alloy-expert` skill, `ti-researcher` agent, redundant Gemini/Codex symlinks)
548
515
  - If run inside a Titanium project, prompts to run `titools sync` afterward
549
- - Warns Gemini users if local skills override existing global Gemini skills
550
516
  - Installs Claude Code SessionStart hook for auto-updates
551
517
 
552
518
  ### titools auto-update
@@ -736,7 +702,7 @@ This pattern is documented across three TiTools skills: `ti-ui`, `ti-expert`, an
736
702
  | Skill | SKILL.md | References |
737
703
  | --------- | ----------------------------- | ----------------------------------------------------- |
738
704
  | ti-expert | Architecture + Implementation | 21 files (patterns, testing, security, etc.) |
739
- | purgetss | Setup + Critical Rules | 29 files (grid, animations, icons, class-index, etc.) |
705
+ | purgetss | Setup + Critical Rules | 33 files (grid, animations, icons, class-index, SVG pipeline, etc.) |
740
706
  | ti-ui | UI Rules + Platform Diffs | 14 files (layouts, lists, gestures, etc.) |
741
707
 
742
708
  ---
package/lib/cleanup.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  getAgentsSkillsDir,
11
11
  getClaudeAgentsDir,
12
12
  getCodexSkillsDir,
13
+ getGeminiSkillsDir,
13
14
  } from './config.js';
14
15
  import { detectPlatforms } from './platform.js';
15
16
  import { existsSync, lstatSync, rmSync } from 'fs';
@@ -54,6 +55,20 @@ export function removeLegacySkillSymlinks(platformSkillsDir) {
54
55
  return removeSkillSymlinks(platformSkillsDir, { legacyOnly: true });
55
56
  }
56
57
 
58
+ // When the user de-selects some skills during an install, anything previously
59
+ // installed for those skills (in ~/.agents/skills/ and as platform symlinks)
60
+ // must be cleaned up so the on-disk state matches the user's selection.
61
+ export function removeUnselectedSkills(baseDir, selectedSkills) {
62
+ const skillsDir = getAgentsSkillsDir(baseDir);
63
+ const unselected = SKILLS.filter((s) => !selectedSkills.includes(s));
64
+ return removeEntriesAtDir(skillsDir, unselected, { recursive: true });
65
+ }
66
+
67
+ export function removeUnselectedSymlinks(platformSkillsDir, selectedSkills) {
68
+ const unselected = SKILLS.filter((s) => !selectedSkills.includes(s));
69
+ return removeEntriesAtDir(platformSkillsDir, unselected, { recursive: true });
70
+ }
71
+
57
72
  // Codex reads skills from the canonical ~/.agents/skills/, so any TiTools-managed
58
73
  // symlinks at ~/.codex/skills/ from earlier versions are redundant and should be
59
74
  // cleaned up. Targets both active and legacy skill names.
@@ -62,6 +77,15 @@ export function removeCodexRedundantSymlinks(baseDir) {
62
77
  return removeSkillSymlinks(codexSkillsDir);
63
78
  }
64
79
 
80
+ // Gemini CLI also auto-discovers skills from ~/.agents/skills/ per the
81
+ // agentskills.io standard. Symlinks at ~/.gemini/skills/ from v3.x and
82
+ // earlier produce "Skill conflict detected" warnings on Gemini startup
83
+ // because the same skill is found in both locations.
84
+ export function removeGeminiRedundantSymlinks(baseDir) {
85
+ const geminiSkillsDir = getGeminiSkillsDir(baseDir);
86
+ return removeSkillSymlinks(geminiSkillsDir);
87
+ }
88
+
65
89
  export function removeSkills(baseDir, options = {}) {
66
90
  const skillsDir = getAgentsSkillsDir(baseDir);
67
91
  const skillList = getSkillList(options);
@@ -84,6 +108,7 @@ export function cleanupLegacyArtifacts(baseDir) {
84
108
  }
85
109
 
86
110
  removeCodexRedundantSymlinks(baseDir);
111
+ removeGeminiRedundantSymlinks(baseDir);
87
112
 
88
113
  if (baseDir) {
89
114
  removeSkills(undefined, { legacyOnly: true });
@@ -93,6 +118,7 @@ export function cleanupLegacyArtifacts(baseDir) {
93
118
  removeLegacySkillSymlinks(platform.skillsDir);
94
119
  }
95
120
  removeCodexRedundantSymlinks(undefined);
121
+ removeGeminiRedundantSymlinks(undefined);
96
122
  }
97
123
  }
98
124
 
@@ -102,6 +128,9 @@ export default {
102
128
  removeSkillSymlinks,
103
129
  removeLegacySkillSymlinks,
104
130
  removeCodexRedundantSymlinks,
131
+ removeGeminiRedundantSymlinks,
132
+ removeUnselectedSkills,
133
+ removeUnselectedSymlinks,
105
134
  removeSkills,
106
135
  removeAgents,
107
136
  cleanupLegacyArtifacts,
@@ -43,10 +43,12 @@ export async function agentsCommand(projectPath, options) {
43
43
  console.log('Run this command from the project root.');
44
44
  process.exit(1);
45
45
  }
46
- // Check for local vs global skills
46
+ // Check for local vs global skills.
47
+ // Gemini CLI auto-discovers from ./.agents/skills/ (agentskills.io standard),
48
+ // so it shares the same local path as AGENTS.md.
47
49
  const localSkillsPaths = {
48
50
  'CLAUDE.md': join(projectDir, '.claude', 'skills'),
49
- 'GEMINI.md': join(projectDir, '.gemini', 'skills'),
51
+ 'GEMINI.md': join(projectDir, '.agents', 'skills'),
50
52
  'AGENTS.md': join(projectDir, '.agents', 'skills'),
51
53
  };
52
54
 
@@ -63,7 +65,7 @@ export async function agentsCommand(projectPath, options) {
63
65
  if (options?.verbose) {
64
66
  console.log(
65
67
  chalk.gray(
66
- `Searched: ${agentsSkillsDir} | ./.agents/skills, ./.claude/skills, ./.gemini/skills`
68
+ `Searched: ${agentsSkillsDir} | ./.agents/skills, ./.claude/skills`
67
69
  )
68
70
  );
69
71
  }
@@ -158,10 +160,9 @@ export async function agentsCommand(projectPath, options) {
158
160
  let rootPath = '~/.agents/skills';
159
161
  const localPath = localSkillsPaths[filename];
160
162
  if (localPath && existsSync(localPath)) {
161
- rootPath = `./${filename === 'CLAUDE.md' ? '.claude' : filename === 'GEMINI.md' ? '.gemini' : '.agents'}/skills`;
162
- } else {
163
- if (filename === 'CLAUDE.md') rootPath = '~/.claude/skills';
164
- if (filename === 'GEMINI.md') rootPath = '~/.gemini/skills';
163
+ rootPath = filename === 'CLAUDE.md' ? './.claude/skills' : './.agents/skills';
164
+ } else if (filename === 'CLAUDE.md') {
165
+ rootPath = '~/.claude/skills';
165
166
  }
166
167
 
167
168
  addOrUpdateBlock(filePath, rootPath);
@@ -22,6 +22,9 @@ import {
22
22
  removeLegacySkillSymlinks,
23
23
  removeSkills,
24
24
  removeAgents,
25
+ removeUnselectedSkills,
26
+ removeUnselectedSymlinks,
27
+ cleanupLegacyArtifacts,
25
28
  } from '../cleanup.js';
26
29
  import {
27
30
  installSkills,
@@ -32,7 +35,12 @@ import { agentsCommand } from './agents.js';
32
35
  import { downloadRepoArchive } from '../downloader.js';
33
36
  import { createSkillSymlinks } from '../symlink.js';
34
37
  import { installHook } from '../hooks.js';
35
- import { formatList, isTitaniumProject } from '../utils.js';
38
+ import {
39
+ formatList,
40
+ isTitaniumProject,
41
+ readSkillDescription,
42
+ shortenSkillDescription,
43
+ } from '../utils.js';
36
44
  import { mkdtemp } from 'fs/promises';
37
45
  import { existsSync } from 'fs';
38
46
  import { join, resolve } from 'path';
@@ -98,6 +106,22 @@ export async function skillsCommand(options) {
98
106
  } else {
99
107
  console.log(chalk.cyan('Mode: Global installation (user home)'));
100
108
  }
109
+ const skillsLocation = isLocal
110
+ ? './.agents/skills/'
111
+ : (customPath ? `${baseDir}/.agents/skills/` : '~/.agents/skills/');
112
+ console.log('');
113
+ console.log(
114
+ chalk.yellow('▸'),
115
+ 'Skills install to',
116
+ chalk.cyan.bold(skillsLocation),
117
+ chalk.dim('(agentskills.io standard)')
118
+ );
119
+ console.log(
120
+ ' ' + chalk.dim('Read directly by Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot, +more.')
121
+ );
122
+ console.log(
123
+ ' ' + chalk.dim('Below: extra symlink mirrors for Claude Code.')
124
+ );
101
125
  console.log('');
102
126
 
103
127
  // Detect installed platforms at the target base directory
@@ -119,9 +143,9 @@ export async function skillsCommand(options) {
119
143
  }
120
144
 
121
145
  if (detectedPlatforms.length === 0 && !options.path && !isLocal) {
122
- console.log(chalk.yellow('No AI coding assistants detected globally.'));
123
- console.log('Install one of: Claude Code or Gemini CLI.');
124
- console.log('(Codex CLI is supported via the canonical ~/.agents/skills/ — no platform-specific symlink needed.)');
146
+ console.log(chalk.yellow('No AI coding assistants detected globally that need platform symlinks.'));
147
+ console.log('Install Claude Code if you want TiTools to create skill symlinks for it.');
148
+ console.log('(Gemini CLI and Codex CLI auto-discover skills from ~/.agents/skills/ — no platform-specific symlink needed.)');
125
149
  console.log('Or use: titools install --local');
126
150
  process.exit(1);
127
151
  }
@@ -186,12 +210,6 @@ export async function skillsCommand(options) {
186
210
  if (removeOnly) {
187
211
  console.log(chalk.yellow('No platforms selected. Removing all platform symlinks and agents.'));
188
212
  }
189
- const showGeminiWarning = (() => {
190
- if (removeOnly || !isLocal) return false;
191
- const globalGemini = globalPlatforms.find((platform) => platform.name === 'gemini');
192
- const geminiSelected = selectedPlatforms.some((platform) => platform.name === 'gemini');
193
- return !!(geminiSelected && globalGemini && hasAnySkillSymlink(globalGemini.skillsDir));
194
- })();
195
213
 
196
214
  // Get repository directory (local or download)
197
215
  const spinner = ora();
@@ -218,6 +236,52 @@ export async function skillsCommand(options) {
218
236
  }
219
237
  }
220
238
 
239
+ // Skill selection prompt — pre-checks all skills; the user can uncheck the
240
+ // ones they don't want. Skipped under --all (CI/automation) and removeOnly.
241
+ // Each line shows the skill name + a one-liner hint in dim gray, like
242
+ // skills.sh, so the rendered row height stays constant and the list does
243
+ // not "jump" when the cursor moves between skills with different-length
244
+ // descriptions.
245
+ let skillsToInstall = SKILLS;
246
+ if (!removeOnly && !options.all && !options.path) {
247
+ const skillChoices = SKILLS.map((name) => {
248
+ const fullDescription = readSkillDescription(repoDir, name);
249
+ const hint = shortenSkillDescription(fullDescription);
250
+ const label = hint
251
+ ? `${name} ${chalk.dim(`(${hint})`)}`
252
+ : name;
253
+ return { name: label, value: name, short: name, checked: true };
254
+ });
255
+
256
+ try {
257
+ const selected = await checkbox({
258
+ message: 'Select skills to install:',
259
+ choices: [...skillChoices, new Separator(' ')],
260
+ theme: {
261
+ style: {
262
+ renderSelectedChoices: () => '',
263
+ prefix: () => chalk.cyan('?'),
264
+ },
265
+ },
266
+ });
267
+
268
+ if (selected.includes('cancel')) {
269
+ console.log('Cancelled.');
270
+ process.exit(0);
271
+ }
272
+
273
+ skillsToInstall = SKILLS.filter((s) => selected.includes(s));
274
+ } catch (error) {
275
+ console.log('\nCancelled.');
276
+ process.exit(0);
277
+ }
278
+
279
+ if (skillsToInstall.length === 0) {
280
+ console.log(chalk.yellow('No skills selected. Nothing to install.'));
281
+ process.exit(0);
282
+ }
283
+ }
284
+
221
285
  const selectedPlatformNames = new Set(selectedPlatforms.map((platform) => platform.name));
222
286
  const removedPlatformNames = [];
223
287
  const claudeSelected = selectedPlatformNames.has('claude');
@@ -226,8 +290,21 @@ export async function skillsCommand(options) {
226
290
  if (!removeOnly) {
227
291
  // Install skills
228
292
  spinner.start('Installing skills...');
229
- const skillsResult = await installSkills(repoDir, baseDir);
230
- spinner.succeed(`${SKILLS.length} skills installed`);
293
+ const skillsResult = await installSkills(repoDir, baseDir, skillsToInstall);
294
+ spinner.succeed(`${skillsToInstall.length} skill${skillsToInstall.length !== 1 ? 's' : ''} installed`);
295
+
296
+ // If the user deselected some skills, sweep any previously-installed
297
+ // copies and their platform symlinks so on-disk state matches selection.
298
+ if (skillsToInstall.length !== SKILLS.length) {
299
+ removeUnselectedSkills(baseDir, skillsToInstall);
300
+ }
301
+
302
+ // Sweep legacy skills, legacy agents, and redundant symlinks at platforms
303
+ // that no longer need TiTools-managed mirrors (Gemini, Codex). Without
304
+ // this, users that ran older TiTools versions keep stale symlinks at
305
+ // ~/.gemini/skills/ and ~/.codex/skills/ that Gemini reports as "Skill
306
+ // conflict detected" warnings on startup.
307
+ cleanupLegacyArtifacts(baseDir);
231
308
 
232
309
  if (claudeSelected) {
233
310
  // Install agents
@@ -251,19 +328,22 @@ export async function skillsCommand(options) {
251
328
  // Create symlinks for selected platforms
252
329
  for (const platform of selectedPlatforms) {
253
330
  removeLegacySkillSymlinks(platform.skillsDir);
331
+ if (skillsToInstall.length !== SKILLS.length) {
332
+ removeUnselectedSymlinks(platform.skillsDir, skillsToInstall);
333
+ }
254
334
  spinner.start(`Linking ${platform.displayName}...`);
255
335
  const symlinkResult = await createSkillSymlinks(
256
336
  platform.skillsDir,
257
- SKILLS,
337
+ skillsToInstall,
258
338
  baseDir
259
339
  );
260
- if (symlinkResult.linked.length === SKILLS.length) {
261
- spinner.succeed(`${platform.displayName}: Skills linked`);
262
- } else {
263
- spinner.warn(
264
- `${platform.displayName}: ${symlinkResult.linked.length}/${SKILLS.length} skills linked`
265
- );
266
- }
340
+ if (symlinkResult.linked.length === skillsToInstall.length) {
341
+ spinner.succeed(`${platform.displayName}: Skills linked`);
342
+ } else {
343
+ spinner.warn(
344
+ `${platform.displayName}: ${symlinkResult.linked.length}/${skillsToInstall.length} skills linked`
345
+ );
346
+ }
267
347
  }
268
348
 
269
349
  // Install Claude Code auto-update hook
@@ -323,12 +403,27 @@ export async function skillsCommand(options) {
323
403
  }
324
404
  }
325
405
 
326
- // Summary
406
+ // Summary — show where the skills landed and which agents read them.
407
+ // This is the "did it actually work" confirmation the user needs.
327
408
  console.log('');
328
409
  console.log(chalk.green('✓ skills sync complete!'));
329
- if (showGeminiWarning) {
410
+ if (!removeOnly) {
330
411
  console.log('');
331
- console.log(chalk.yellow('⚠ Gemini CLI: local skills will override global skills for this project.'));
412
+ console.log(
413
+ ' ' + chalk.bold(skillsLocation) + chalk.dim(' (') +
414
+ chalk.dim(skillsToInstall.length + ' skill' + (skillsToInstall.length !== 1 ? 's' : '')) +
415
+ chalk.dim(')')
416
+ );
417
+ console.log(
418
+ ' ' + chalk.dim('• universal: ') +
419
+ 'Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot ' + chalk.dim('+more')
420
+ );
421
+ if (claudeSelected) {
422
+ console.log(
423
+ ' ' + chalk.dim('• symlinked: ') +
424
+ 'Claude Code ' + chalk.dim('(~/.claude/skills/)')
425
+ );
426
+ }
332
427
  }
333
428
  console.log('');
334
429
  const isProjectDir = isTitaniumProject(process.cwd());
package/lib/config.js CHANGED
@@ -33,18 +33,17 @@ export const SKILLS = [
33
33
  'ti-expert',
34
34
  'purgetss',
35
35
  'ti-ui',
36
- ];
37
- // Legacy skills to remove during updates/uninstall
38
- // ti-api, ti-guides, ti-howtos, alloy-guides, alloy-howtos moved to https://github.com/tidev/skills in v3.0.0
39
- export const LEGACY_SKILLS = [
40
- 'alloy-expert',
41
- 'ti-branding',
42
36
  'ti-api',
43
37
  'ti-guides',
44
38
  'ti-howtos',
45
39
  'alloy-guides',
46
40
  'alloy-howtos',
47
41
  ];
42
+ // Legacy skills to remove during updates/uninstall
43
+ export const LEGACY_SKILLS = [
44
+ 'alloy-expert',
45
+ 'ti-branding',
46
+ ];
48
47
 
49
48
  // Agents to install
50
49
  export const AGENTS = [
@@ -65,6 +64,14 @@ export const getCodexSkillsDir = (baseDir = os.homedir()) => path.join(baseDir,
65
64
  export const getConfigDir = () => path.join(os.homedir(), '.titools');
66
65
 
67
66
  // AI platform detection
67
+ //
68
+ // Only platforms that need TiTools-managed symlinks appear here.
69
+ // Gemini CLI and Codex CLI auto-discover skills from the canonical
70
+ // ~/.agents/skills/ per the agentskills.io standard, so creating
71
+ // platform-specific symlinks at ~/.gemini/skills/ or ~/.codex/skills/
72
+ // would be redundant — and in Gemini's case actively harmful, since it
73
+ // reads both locations and reports "Skill conflict detected" warnings
74
+ // when the same skill exists in both.
68
75
  export const getPlatforms = (baseDir = os.homedir()) => [
69
76
  {
70
77
  name: 'claude',
@@ -72,15 +79,6 @@ export const getPlatforms = (baseDir = os.homedir()) => [
72
79
  skillsDir: getClaudeSkillsDir(baseDir),
73
80
  configDir: path.join(baseDir, '.claude'),
74
81
  },
75
- {
76
- name: 'gemini',
77
- displayName: 'Gemini CLI',
78
- skillsDir: getGeminiSkillsDir(baseDir),
79
- configDir: path.join(baseDir, '.gemini'),
80
- },
81
- // Codex CLI is intentionally omitted: it auto-discovers skills from the
82
- // canonical ~/.agents/skills/ per the agentskills.io standard, so a
83
- // platform-specific symlink at ~/.codex/skills/ would be redundant.
84
82
  ];
85
83
 
86
84
  // Files to install
package/lib/installer.js CHANGED
@@ -69,12 +69,14 @@ export async function installSkill(repoDir, skillName, baseDir = os.homedir()) {
69
69
  }
70
70
 
71
71
  /**
72
- * Install all skills to the agents skills directory
72
+ * Install skills to the agents skills directory
73
73
  * @param {string} repoDir - Repository directory
74
74
  * @param {string} baseDir - Base directory for installation
75
+ * @param {string[]} [skillsToInstall] - Subset of skills to install (defaults to all SKILLS)
75
76
  * @returns {Promise<Object>} Results object with success/failure counts
76
77
  */
77
- export async function installSkills(repoDir, baseDir = os.homedir()) {
78
+ export async function installSkills(repoDir, baseDir = os.homedir(), skillsToInstall) {
79
+ const list = skillsToInstall || SKILLS;
78
80
  const results = {
79
81
  installed: [],
80
82
  failed: [],
@@ -91,7 +93,7 @@ export async function installSkills(repoDir, baseDir = os.homedir()) {
91
93
  results.failed.push(...legacyGlobal.failed);
92
94
  }
93
95
 
94
- for (const skill of SKILLS) {
96
+ for (const skill of list) {
95
97
  if (await installSkill(repoDir, skill, baseDir)) {
96
98
  results.installed.push(skill);
97
99
  } else {
package/lib/utils.js CHANGED
@@ -14,6 +14,42 @@ const __dirname = dirname(__filename);
14
14
  const packageRoot = resolve(__dirname, '..');
15
15
  const packageSkillsDir = join(packageRoot, 'skills');
16
16
 
17
+ /**
18
+ * Read the description field from a skill's SKILL.md frontmatter.
19
+ * @param {string} repoDir - Repository directory containing skills/
20
+ * @param {string} skillName - Skill name (directory under skills/)
21
+ * @returns {string} Description text, or empty string if not available.
22
+ */
23
+ export function readSkillDescription(repoDir, skillName) {
24
+ try {
25
+ const skillPath = join(repoDir, 'skills', skillName, 'SKILL.md');
26
+ const content = readFileSync(skillPath, 'utf8');
27
+ const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
28
+ if (!fmMatch) return '';
29
+ const descMatch = fmMatch[1].match(/^description:\s*["']?(.+?)["']?\s*$/m);
30
+ return descMatch ? descMatch[1].trim() : '';
31
+ } catch {
32
+ return '';
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Compress a skill description to a one-line summary suitable for an inline
38
+ * checkbox preview. Picks the first segment of the description and trims it
39
+ * to `maxLen` characters, appending an ellipsis when the source is longer.
40
+ *
41
+ * @param {string} description - The full SKILL.md description.
42
+ * @param {number} maxLen - Maximum characters of the output (excluding ellipsis).
43
+ * @returns {string} Trimmed one-liner, or empty string if input was empty.
44
+ */
45
+ export function shortenSkillDescription(description, maxLen = 60) {
46
+ if (!description) return '';
47
+ let firstSegment = description.split(/\s*[—.:;]\s*/)[0].trim();
48
+ firstSegment = firstSegment.replace(/\s+/g, ' ');
49
+ if (firstSegment.length <= maxLen) return firstSegment;
50
+ return firstSegment.slice(0, maxLen).trimEnd() + '…';
51
+ }
52
+
17
53
  /**
18
54
  * Check if Titanium knowledge block exists in a file
19
55
  * @param {string} filePath - Path to the file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/titools",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
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",