@maccesar/titools 4.1.0 → 4.3.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.
- package/README.md +22 -5
- package/bin/titools.js +4 -4
- package/commands/ti-audit.md +13 -0
- package/commands/ti-check.md +15 -0
- package/commands/ti-new-screen.md +18 -0
- package/lib/claude-plugin.js +105 -0
- package/lib/cleanup.js +18 -0
- package/lib/commands/doctor.js +75 -3
- package/lib/commands/skills.js +66 -6
- package/lib/commands/uninstall.js +32 -2
- package/lib/commands/update.js +15 -0
- package/lib/config.js +37 -0
- package/lib/installer.js +85 -1
- package/lib/symlink.js +40 -3
- package/package.json +2 -1
- package/skills/alloy-guides/references/MODELS.md +23 -0
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +19 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +12 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +18 -0
- package/skills/purgetss/references/EXAMPLES.md +24 -0
- package/skills/purgetss/references/animation-advanced.md +12 -0
- package/skills/purgetss/references/animation-system.md +23 -0
- package/skills/purgetss/references/app-branding.md +23 -0
- package/skills/purgetss/references/apply-directive.md +17 -0
- package/skills/purgetss/references/arbitrary-values.md +13 -0
- package/skills/purgetss/references/class-index-properties.md +11 -0
- package/skills/purgetss/references/cli-commands.md +27 -0
- package/skills/purgetss/references/customization-deep-dive.md +15 -0
- package/skills/purgetss/references/dynamic-component-creation.md +16 -0
- package/skills/purgetss/references/migration-guide.md +20 -0
- package/skills/purgetss/references/multi-density-images.md +27 -0
- package/skills/purgetss/references/semantic-colors.md +15 -0
- package/skills/purgetss/references/tikit-components.md +17 -0
- package/skills/purgetss/references/titanium-resets.md +16 -0
- package/skills/purgetss/references/ui-ux-design.md +20 -0
- package/skills/ti-api/references/api-android.md +23 -0
- package/skills/ti-api/references/api-app-platform.md +24 -0
- package/skills/ti-api/references/api-core.md +24 -0
- package/skills/ti-api/references/api-data-network.md +21 -0
- package/skills/ti-api/references/api-media.md +15 -1
- package/skills/ti-api/references/api-modules-ble-bluetooth.md +26 -0
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +18 -0
- package/skills/ti-api/references/api-modules-map.md +21 -2
- package/skills/ti-api/references/api-modules-nfc.md +34 -0
- package/skills/ti-api/references/api-modules-social-misc.md +22 -0
- package/skills/ti-api/references/api-services.md +21 -1
- package/skills/ti-api/references/api-ui-android.md +18 -0
- package/skills/ti-api/references/api-ui-extras.md +19 -1
- package/skills/ti-api/references/api-ui-ios-animator.md +14 -0
- package/skills/ti-api/references/api-ui-ios.md +42 -0
- package/skills/ti-api/references/api-ui-lists.md +14 -2
- package/skills/ti-api/references/api-ui-text-input.md +15 -0
- package/skills/ti-api/references/api-ui-views.md +19 -0
- package/skills/ti-api/references/api-ui-windows-navigation.md +13 -0
- package/skills/ti-api/references/api-xml-global.md +31 -0
- package/skills/ti-expert/references/adaptive-layouts.md +14 -0
- package/skills/ti-expert/references/alloy-builtins.md +15 -0
- package/skills/ti-expert/references/alloy-structure.md +15 -0
- package/skills/ti-expert/references/code-conventions.md +19 -0
- package/skills/ti-expert/references/controller-patterns.md +18 -0
- package/skills/ti-expert/references/error-handling.md +16 -0
- package/skills/ti-expert/references/examples.md +17 -0
- package/skills/ti-expert/references/migration-patterns.md +15 -0
- package/skills/ti-expert/references/patterns.md +16 -0
- package/skills/ti-expert/references/performance-optimization.md +17 -0
- package/skills/ti-expert/references/security-device.md +13 -0
- package/skills/ti-expert/references/state-management.md +16 -0
- package/skills/ti-expert/references/testing-e2e-ci.md +17 -0
- package/skills/ti-expert/references/testing-unit.md +20 -0
- package/skills/ti-expert/references/theming.md +20 -0
- package/skills/ti-guides/references/app-distribution.md +34 -0
- package/skills/ti-guides/references/application-frameworks.md +13 -0
- package/skills/ti-guides/references/cli-reference.md +16 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +21 -0
- package/skills/ti-guides/references/javascript-primer.md +12 -0
- package/skills/ti-guides/references/tiapp-config.md +15 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +15 -0
- package/skills/ti-howtos/references/cross-platform-development.md +16 -0
- package/skills/ti-howtos/references/debugging-profiling.md +15 -0
- package/skills/ti-howtos/references/extending-titanium.md +14 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +16 -0
- package/skills/ti-howtos/references/local-data-sources.md +32 -0
- package/skills/ti-howtos/references/notification-services.md +12 -0
- package/skills/ti-howtos/references/remote-data-sources.md +15 -0
- package/skills/ti-howtos/references/tutorials.md +13 -0
- package/skills/ti-ui/references/animation-and-matrices.md +17 -0
- package/skills/ti-ui/references/application-structures.md +19 -0
- package/skills/ti-ui/references/custom-fonts-styling.md +18 -0
- package/skills/ti-ui/references/event-handling.md +16 -0
- package/skills/ti-ui/references/gestures.md +17 -0
- package/skills/ti-ui/references/icons-and-splash-screens.md +17 -0
- package/skills/ti-ui/references/layouts-and-positioning.md +16 -0
- package/skills/ti-ui/references/listviews-and-performance.md +21 -0
- package/skills/ti-ui/references/orientation.md +20 -0
- package/skills/ti-ui/references/platform-ui-android.md +20 -0
- package/skills/ti-ui/references/platform-ui-ios.md +17 -0
- package/skills/ti-ui/references/tableviews.md +24 -0
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
removeSkillSymlinks,
|
|
17
17
|
removeSkills,
|
|
18
18
|
removeAgents,
|
|
19
|
+
removeCommands,
|
|
19
20
|
} from '../cleanup.js';
|
|
20
21
|
import checkbox, { Separator } from '../prompts/checkboxCancel.js';
|
|
21
22
|
import {
|
|
@@ -25,8 +26,8 @@ import {
|
|
|
25
26
|
} from '../utils.js';
|
|
26
27
|
import { existsSync } from 'fs';
|
|
27
28
|
import { join, resolve } from 'path';
|
|
28
|
-
import { getAgentsSkillsDir, getClaudeAgentsDir } from '../config.js';
|
|
29
|
-
import { getSkillList, getAgentList } from '../cleanup.js';
|
|
29
|
+
import { getAgentsSkillsDir, getClaudeAgentsDir, getClaudeCommandsDir } from '../config.js';
|
|
30
|
+
import { getSkillList, getAgentList, getCommandList } from '../cleanup.js';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Uninstall command handler
|
|
@@ -56,10 +57,13 @@ export async function uninstallCommand(options) {
|
|
|
56
57
|
|
|
57
58
|
const skillList = getSkillList();
|
|
58
59
|
const agentList = getAgentList().map((agent) => `${agent}.md`);
|
|
60
|
+
const commandList = getCommandList().map((command) => `${command}.md`);
|
|
59
61
|
const homeSkillsDir = getAgentsSkillsDir();
|
|
60
62
|
const projectSkillsDir = getAgentsSkillsDir(projectDir);
|
|
61
63
|
const homeAgentsDir = getClaudeAgentsDir();
|
|
62
64
|
const projectAgentsDir = getClaudeAgentsDir(projectDir);
|
|
65
|
+
const homeCommandsDir = getClaudeCommandsDir();
|
|
66
|
+
const projectCommandsDir = getClaudeCommandsDir(projectDir);
|
|
63
67
|
|
|
64
68
|
const hasAnyInDir = (dir, names) =>
|
|
65
69
|
!!dir && existsSync(dir) && names.some((name) => existsSync(join(dir, name)));
|
|
@@ -68,6 +72,8 @@ export async function uninstallCommand(options) {
|
|
|
68
72
|
const hasProjectSkills = isProject && hasAnyInDir(projectSkillsDir, skillList);
|
|
69
73
|
const hasHomeAgents = hasAnyInDir(homeAgentsDir, agentList);
|
|
70
74
|
const hasProjectAgents = isProject && hasAnyInDir(projectAgentsDir, agentList);
|
|
75
|
+
const hasHomeCommands = hasAnyInDir(homeCommandsDir, commandList);
|
|
76
|
+
const hasProjectCommands = isProject && hasAnyInDir(projectCommandsDir, commandList);
|
|
71
77
|
|
|
72
78
|
const hasHomeSymlinks = detectedPlatforms.some((platform) =>
|
|
73
79
|
hasAnyInDir(platform.skillsDir, skillList)
|
|
@@ -80,6 +86,13 @@ export async function uninstallCommand(options) {
|
|
|
80
86
|
if (hasHomeAgents || hasProjectAgents) {
|
|
81
87
|
choices.push({ name: '`ti-pro` for Claude Code', value: 'agents', checked: true });
|
|
82
88
|
}
|
|
89
|
+
if (hasHomeCommands || hasProjectCommands) {
|
|
90
|
+
choices.push({
|
|
91
|
+
name: 'Slash commands for Claude Code (`/ti-check`, `/ti-new-screen`, `/ti-audit`)',
|
|
92
|
+
value: 'commands',
|
|
93
|
+
checked: true,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
83
96
|
if (hasKnowledgeBlocks) {
|
|
84
97
|
choices.push({ name: 'Knowledge index from context files', value: 'knowledge', checked: true });
|
|
85
98
|
}
|
|
@@ -207,6 +220,23 @@ export async function uninstallCommand(options) {
|
|
|
207
220
|
}
|
|
208
221
|
}
|
|
209
222
|
|
|
223
|
+
if (targets.includes('commands')) {
|
|
224
|
+
spinner.start('Removing slash commands...');
|
|
225
|
+
const results = [];
|
|
226
|
+
results.push(removeCommands(undefined));
|
|
227
|
+
if (isProject) {
|
|
228
|
+
results.push(removeCommands(projectDir));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const removed = results.flatMap((r) => r.removed);
|
|
232
|
+
if (removed.length > 0) {
|
|
233
|
+
spinner.succeed(`${removed.length} slash command${removed.length !== 1 ? 's' : ''} removed`);
|
|
234
|
+
actionTaken = true;
|
|
235
|
+
} else {
|
|
236
|
+
spinner.info('No slash commands to remove');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
210
240
|
// Remove knowledge index blocks from project files
|
|
211
241
|
if (targets.includes('knowledge')) {
|
|
212
242
|
if (!isProject) {
|
package/lib/commands/update.js
CHANGED
|
@@ -18,6 +18,7 @@ import { cleanupLegacyArtifacts, getSkillList } from '../cleanup.js';
|
|
|
18
18
|
import {
|
|
19
19
|
installSkills,
|
|
20
20
|
installAgents,
|
|
21
|
+
installCommands,
|
|
21
22
|
getLocalRepoDir,
|
|
22
23
|
} from '../installer.js';
|
|
23
24
|
import { agentsCommand } from './agents.js';
|
|
@@ -72,6 +73,20 @@ async function performUpdate(baseDir, repoDir, spinner) {
|
|
|
72
73
|
} else {
|
|
73
74
|
spinner.info('No agents to sync');
|
|
74
75
|
}
|
|
76
|
+
|
|
77
|
+
spinner.start('Syncing slash commands...');
|
|
78
|
+
const commandsResult = await installCommands(repoDir, baseDir);
|
|
79
|
+
if (commandsResult.installed.length > 0) {
|
|
80
|
+
spinner.succeed(
|
|
81
|
+
`${commandsResult.installed.length} slash command${commandsResult.installed.length !== 1 ? 's' : ''} synced`
|
|
82
|
+
);
|
|
83
|
+
} else if (commandsResult.skipped.length > 0) {
|
|
84
|
+
spinner.info(
|
|
85
|
+
`${commandsResult.skipped.length} slash command${commandsResult.skipped.length !== 1 ? 's' : ''} already provided by the marketplace plugin`
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
spinner.info('No slash commands to sync');
|
|
89
|
+
}
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
cleanupLegacyArtifacts(baseDir);
|
package/lib/config.js
CHANGED
|
@@ -54,15 +54,44 @@ export const LEGACY_AGENTS = [
|
|
|
54
54
|
'ti-researcher',
|
|
55
55
|
];
|
|
56
56
|
|
|
57
|
+
// Slash commands to install (Claude Code only — copied to ~/.claude/commands/)
|
|
58
|
+
export const COMMANDS = [
|
|
59
|
+
'ti-check',
|
|
60
|
+
'ti-new-screen',
|
|
61
|
+
'ti-audit',
|
|
62
|
+
];
|
|
63
|
+
// Legacy commands to remove during updates/uninstall
|
|
64
|
+
export const LEGACY_COMMANDS = [];
|
|
65
|
+
|
|
57
66
|
// Directory paths
|
|
58
67
|
export const getAgentsDir = (baseDir = os.homedir()) => path.join(baseDir, '.agents');
|
|
59
68
|
export const getAgentsSkillsDir = (baseDir = os.homedir()) => path.join(getAgentsDir(baseDir), 'skills');
|
|
60
69
|
export const getClaudeAgentsDir = (baseDir = os.homedir()) => path.join(baseDir, '.claude', 'agents');
|
|
61
70
|
export const getClaudeSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.claude', 'skills');
|
|
71
|
+
export const getClaudeCommandsDir = (baseDir = os.homedir()) => path.join(baseDir, '.claude', 'commands');
|
|
62
72
|
export const getGeminiSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.gemini', 'skills');
|
|
63
73
|
export const getCodexSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.codex', 'skills');
|
|
64
74
|
export const getConfigDir = () => path.join(os.homedir(), '.titools');
|
|
65
75
|
|
|
76
|
+
// Name of the Claude marketplace and plugin where this CLI publishes itself.
|
|
77
|
+
// Used to detect when a skill or command already reaches Claude Code through the
|
|
78
|
+
// marketplace plugin, so the CLI does not install a second copy of it.
|
|
79
|
+
export const CLAUDE_PLUGIN_MARKETPLACE = 'maccesar-titools';
|
|
80
|
+
export const CLAUDE_PLUGIN_NAME = 'titools';
|
|
81
|
+
export const getClaudePluginSkillsPath = (baseDir = os.homedir()) =>
|
|
82
|
+
path.join(baseDir, '.claude', 'plugins', 'cache', CLAUDE_PLUGIN_MARKETPLACE, CLAUDE_PLUGIN_NAME);
|
|
83
|
+
|
|
84
|
+
// The key Claude Code writes under "enabledPlugins" when the plugin is installed.
|
|
85
|
+
export const CLAUDE_PLUGIN_KEY = `${CLAUDE_PLUGIN_NAME}@${CLAUDE_PLUGIN_MARKETPLACE}`;
|
|
86
|
+
|
|
87
|
+
// Where Claude Code records which plugins are enabled. Both files are consulted
|
|
88
|
+
// because the local variant overrides the shared one, and either may carry the
|
|
89
|
+
// entry depending on how the plugin was installed.
|
|
90
|
+
export const getClaudeSettingsPaths = (baseDir = os.homedir()) => [
|
|
91
|
+
path.join(baseDir, '.claude', 'settings.json'),
|
|
92
|
+
path.join(baseDir, '.claude', 'settings.local.json'),
|
|
93
|
+
];
|
|
94
|
+
|
|
66
95
|
// AI platform detection
|
|
67
96
|
//
|
|
68
97
|
// Only platforms that need TiTools-managed symlinks appear here.
|
|
@@ -111,14 +140,22 @@ export default {
|
|
|
111
140
|
LEGACY_SKILLS,
|
|
112
141
|
AGENTS,
|
|
113
142
|
LEGACY_AGENTS,
|
|
143
|
+
COMMANDS,
|
|
144
|
+
LEGACY_COMMANDS,
|
|
114
145
|
getAgentsDir,
|
|
115
146
|
getAgentsSkillsDir,
|
|
116
147
|
getClaudeAgentsDir,
|
|
117
148
|
getClaudeSkillsDir,
|
|
149
|
+
getClaudeCommandsDir,
|
|
118
150
|
getGeminiSkillsDir,
|
|
119
151
|
getCodexSkillsDir,
|
|
120
152
|
getConfigDir,
|
|
121
153
|
getPlatforms,
|
|
154
|
+
CLAUDE_PLUGIN_MARKETPLACE,
|
|
155
|
+
CLAUDE_PLUGIN_NAME,
|
|
156
|
+
CLAUDE_PLUGIN_KEY,
|
|
157
|
+
getClaudePluginSkillsPath,
|
|
158
|
+
getClaudeSettingsPaths,
|
|
122
159
|
AI_FILE_PRIORITIES,
|
|
123
160
|
TITANIUM_PROJECT_FILE,
|
|
124
161
|
GITHUB_API_HEADERS,
|
package/lib/installer.js
CHANGED
|
@@ -15,10 +15,12 @@ import os from 'os';
|
|
|
15
15
|
import {
|
|
16
16
|
SKILLS,
|
|
17
17
|
AGENTS,
|
|
18
|
+
COMMANDS,
|
|
18
19
|
getAgentsSkillsDir,
|
|
19
20
|
getClaudeAgentsDir,
|
|
21
|
+
getClaudeCommandsDir,
|
|
20
22
|
} from './config.js';
|
|
21
|
-
import { removeSkills, removeAgents } from './cleanup.js';
|
|
23
|
+
import { removeSkills, removeAgents, removeCommands } from './cleanup.js';
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Recursively copy a directory
|
|
@@ -170,6 +172,86 @@ export async function installAgents(repoDir, baseDir = os.homedir()) {
|
|
|
170
172
|
return results;
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Install a single slash command to the Claude commands directory
|
|
177
|
+
* @param {string} repoDir - Repository directory
|
|
178
|
+
* @param {string} commandName - Name of the command (without .md)
|
|
179
|
+
* @param {string} baseDir - Base directory for installation
|
|
180
|
+
* @returns {Promise<boolean>} True if installed successfully
|
|
181
|
+
*/
|
|
182
|
+
export async function installCommand(repoDir, commandName, baseDir = os.homedir()) {
|
|
183
|
+
const commandsDir = getClaudeCommandsDir(baseDir);
|
|
184
|
+
const src = join(repoDir, 'commands', `${commandName}.md`);
|
|
185
|
+
const dest = join(commandsDir, `${commandName}.md`);
|
|
186
|
+
|
|
187
|
+
if (!existsSync(commandsDir)) {
|
|
188
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!existsSync(src)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (existsSync(dest)) {
|
|
196
|
+
await remove(dest);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
copyFileSync(src, dest);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Install all slash commands to the Claude commands directory
|
|
205
|
+
* @param {string} repoDir - Repository directory
|
|
206
|
+
* @param {string} baseDir - Base directory for installation
|
|
207
|
+
* @returns {Promise<Object>} Results object with installed / failed / removed / skipped
|
|
208
|
+
*/
|
|
209
|
+
export async function installCommands(repoDir, baseDir = os.homedir()) {
|
|
210
|
+
const results = {
|
|
211
|
+
installed: [],
|
|
212
|
+
failed: [],
|
|
213
|
+
removed: [],
|
|
214
|
+
skipped: [],
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const legacyLocal = removeCommands(baseDir, { legacyOnly: true });
|
|
218
|
+
results.removed.push(...legacyLocal.removed);
|
|
219
|
+
results.failed.push(...legacyLocal.failed);
|
|
220
|
+
|
|
221
|
+
if (baseDir && baseDir !== os.homedir()) {
|
|
222
|
+
const legacyGlobal = removeCommands(undefined, { legacyOnly: true });
|
|
223
|
+
results.removed.push(...legacyGlobal.removed);
|
|
224
|
+
results.failed.push(...legacyGlobal.failed);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const { pluginProvidesCommand } = await import('./claude-plugin.js');
|
|
228
|
+
const commandsDir = getClaudeCommandsDir(baseDir);
|
|
229
|
+
|
|
230
|
+
for (const cmd of COMMANDS) {
|
|
231
|
+
// Same rule the symlink step applies to skills: when the marketplace plugin
|
|
232
|
+
// already provides the command, installing our own copy makes it show up
|
|
233
|
+
// twice in the autocomplete. Clean up any copy left from before the plugin
|
|
234
|
+
// was installed.
|
|
235
|
+
if (pluginProvidesCommand(cmd, baseDir)) {
|
|
236
|
+
const stalePath = join(commandsDir, `${cmd}.md`);
|
|
237
|
+
if (existsSync(stalePath)) {
|
|
238
|
+
await remove(stalePath);
|
|
239
|
+
results.removed.push(cmd);
|
|
240
|
+
}
|
|
241
|
+
results.skipped.push(cmd);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (await installCommand(repoDir, cmd, baseDir)) {
|
|
246
|
+
results.installed.push(cmd);
|
|
247
|
+
} else {
|
|
248
|
+
results.failed.push(cmd);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
254
|
+
|
|
173
255
|
/**
|
|
174
256
|
* Get the local repository directory if running from source
|
|
175
257
|
* @returns {string|null} Local repo directory or null
|
|
@@ -191,5 +273,7 @@ export default {
|
|
|
191
273
|
installSkills,
|
|
192
274
|
installAgent,
|
|
193
275
|
installAgents,
|
|
276
|
+
installCommand,
|
|
277
|
+
installCommands,
|
|
194
278
|
getLocalRepoDir,
|
|
195
279
|
};
|
package/lib/symlink.js
CHANGED
|
@@ -69,19 +69,44 @@ async function removePath(path) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
72
|
+
* Whether the marketplace plugin already provides this skill to Claude Code.
|
|
73
|
+
*
|
|
74
|
+
* Claude Code lists skills the plugin carries on its own, so an additional
|
|
75
|
+
* symlink at ~/.claude/skills/<skill> produces a duplicate entry in the
|
|
76
|
+
* autocomplete. This helper lets the symlink step skip Claude when the plugin
|
|
77
|
+
* already covers it.
|
|
78
|
+
*
|
|
79
|
+
* Requires the plugin to be *enabled*, not merely cached — see `claude-plugin.js`
|
|
80
|
+
* for why the distinction matters.
|
|
81
|
+
* @param {string} skillName - Skill name (e.g. 'ti-expert')
|
|
82
|
+
* @param {string} baseDir - Optional base directory (defaults to homedir via config)
|
|
83
|
+
* @returns {Promise<boolean>} True if the plugin provides this skill
|
|
84
|
+
*/
|
|
85
|
+
export async function isClaudePluginSkillInstalled(skillName, baseDir) {
|
|
86
|
+
const { pluginProvidesSkill } = await import('./claude-plugin.js');
|
|
87
|
+
return pluginProvidesSkill(skillName, baseDir);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create symlinks for all skills to a platform directory.
|
|
92
|
+
* For the Claude platform, skills already provided by the maccesar-titools
|
|
93
|
+
* marketplace plugin are skipped to avoid duplicate entries; if a stale symlink
|
|
94
|
+
* from a previous install exists, it is removed.
|
|
73
95
|
* @param {string} platformSkillsDir - Platform skills directory
|
|
74
96
|
* @param {Array} skills - List of skill names
|
|
75
97
|
* @param {string} baseDir - Optional base directory for target resolution
|
|
76
|
-
* @returns {Promise<Object>} Results object with
|
|
98
|
+
* @returns {Promise<Object>} Results object with linked / failed / skipped arrays
|
|
77
99
|
*/
|
|
78
100
|
export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
|
|
79
|
-
const { getAgentsSkillsDir } = await import('./config.js');
|
|
101
|
+
const { getAgentsSkillsDir, getClaudeSkillsDir } = await import('./config.js');
|
|
80
102
|
const agentsSkillsDir = getAgentsSkillsDir(baseDir);
|
|
103
|
+
const claudeSkillsDir = getClaudeSkillsDir(baseDir);
|
|
104
|
+
const isClaudePlatform = platformSkillsDir === claudeSkillsDir;
|
|
81
105
|
|
|
82
106
|
const results = {
|
|
83
107
|
linked: [],
|
|
84
108
|
failed: [],
|
|
109
|
+
skipped: [],
|
|
85
110
|
};
|
|
86
111
|
|
|
87
112
|
// Ensure platform directory exists
|
|
@@ -93,6 +118,17 @@ export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
|
|
|
93
118
|
const useRelative = !!baseDir;
|
|
94
119
|
|
|
95
120
|
for (const skill of skills) {
|
|
121
|
+
// Skip Claude when the marketplace plugin already provides this skill;
|
|
122
|
+
// clean up any stale symlink left from a previous CLI install.
|
|
123
|
+
if (isClaudePlatform && await isClaudePluginSkillInstalled(skill, baseDir)) {
|
|
124
|
+
const stalePath = join(platformSkillsDir, skill);
|
|
125
|
+
if (existsSync(stalePath)) {
|
|
126
|
+
await removePath(stalePath);
|
|
127
|
+
}
|
|
128
|
+
results.skipped.push(skill);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
96
132
|
const target = join(agentsSkillsDir, skill);
|
|
97
133
|
const linkPath = join(platformSkillsDir, skill);
|
|
98
134
|
|
|
@@ -147,6 +183,7 @@ export async function updateSymlink(target, path) {
|
|
|
147
183
|
|
|
148
184
|
export default {
|
|
149
185
|
createSymlinkOrCopy,
|
|
186
|
+
isClaudePluginSkillInstalled,
|
|
150
187
|
createSkillSymlinks,
|
|
151
188
|
isSymlink,
|
|
152
189
|
resolveSymlink,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maccesar/titools",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.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",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"lib/",
|
|
58
58
|
"skills/",
|
|
59
59
|
"agents/",
|
|
60
|
+
"commands/",
|
|
60
61
|
"AGENTS-VERCEL-RESEARCH.md"
|
|
61
62
|
],
|
|
62
63
|
"publishConfig": {
|
|
@@ -6,6 +6,29 @@ Models, collections, and how to bind them to views in Alloy. For sync adapters,
|
|
|
6
6
|
2. [Alloy Collection and Model Objects](#alloy-collection-and-model-objects)
|
|
7
7
|
3. [Alloy Data Binding](#alloy-data-binding)
|
|
8
8
|
|
|
9
|
+
<!-- TOC-START -->
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Overview](#overview)
|
|
13
|
+
- [Alloy Collection and Model Objects](#alloy-collection-and-model-objects)
|
|
14
|
+
- [Models](#models)
|
|
15
|
+
- [Collections](#collections)
|
|
16
|
+
- [Event Handling](#event-handling)
|
|
17
|
+
- [Alloy Data Binding](#alloy-data-binding)
|
|
18
|
+
- [Introduction](#introduction)
|
|
19
|
+
- [Alloy Binding](#alloy-binding)
|
|
20
|
+
- [Collection vs Model Data Binding](#collection-vs-model-data-binding)
|
|
21
|
+
- [Global Singleton vs Local Instance](#global-singleton-vs-local-instance)
|
|
22
|
+
- [Simple vs Complex Data Binding](#simple-vs-complex-data-binding)
|
|
23
|
+
- [Backbone Binding](#backbone-binding)
|
|
24
|
+
- [Bind Deep Object Properties](#bind-deep-object-properties)
|
|
25
|
+
- [Use Models and Properties with Special Characters](#use-models-and-properties-with-special-characters)
|
|
26
|
+
- [Bind Multiple Models to the Same View](#bind-multiple-models-to-the-same-view)
|
|
27
|
+
- [Define Transformations in the Model](#define-transformations-in-the-model)
|
|
28
|
+
- [Tips and Tricks](#tips-and-tricks)
|
|
29
|
+
|
|
30
|
+
<!-- TOC-END -->
|
|
31
|
+
|
|
9
32
|
## Overview
|
|
10
33
|
|
|
11
34
|
Alloy uses Backbone.js to provide support for its models and collections. Alloy also borrows the concepts of migrations and adapters from Rails for storage integration.
|
|
@@ -6,6 +6,25 @@ Persistence, custom adapters, schema migrations, plain Backbone usage, and the B
|
|
|
6
6
|
2. [Backbone Objects without Alloy](#backbone-objects-without-alloy)
|
|
7
7
|
3. [Alloy Backbone Migration](#alloy-backbone-migration)
|
|
8
8
|
|
|
9
|
+
<!-- TOC-START -->
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Alloy Sync Adapters and Migrations](#alloy-sync-adapters-and-migrations)
|
|
13
|
+
- [Sync Adapters](#sync-adapters)
|
|
14
|
+
- [Custom Sync Adapters](#custom-sync-adapters)
|
|
15
|
+
- [Migrations](#migrations)
|
|
16
|
+
- [Backbone Objects without Alloy](#backbone-objects-without-alloy)
|
|
17
|
+
- [Using Backbone Objects with Alloy Data Binding](#using-backbone-objects-with-alloy-data-binding)
|
|
18
|
+
- [Alloy Backbone Migration](#alloy-backbone-migration)
|
|
19
|
+
- [Overview](#overview)
|
|
20
|
+
- [Setup](#setup)
|
|
21
|
+
- [Summary of Changes](#summary-of-changes)
|
|
22
|
+
- [Parse Method](#parse-method)
|
|
23
|
+
- [Silent Option](#silent-option)
|
|
24
|
+
- [API Changes](#api-changes)
|
|
25
|
+
|
|
26
|
+
<!-- TOC-END -->
|
|
27
|
+
|
|
9
28
|
## Alloy Sync Adapters and Migrations
|
|
10
29
|
|
|
11
30
|
### Sync Adapters
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Alloy Styles and Themes
|
|
2
2
|
|
|
3
|
+
<!-- TOC-START -->
|
|
4
|
+
## Contents
|
|
5
|
+
|
|
6
|
+
- [Titanium Style Sheets](#titanium-style-sheets)
|
|
7
|
+
- [Global Styles](#global-styles)
|
|
8
|
+
- [Platform-Specific Styles](#platform-specific-styles)
|
|
9
|
+
- [Custom Query Styles](#custom-query-styles)
|
|
10
|
+
- [Themes](#themes)
|
|
11
|
+
- [Style Priorities](#style-priorities)
|
|
12
|
+
|
|
13
|
+
<!-- TOC-END -->
|
|
14
|
+
|
|
3
15
|
## Titanium Style Sheets
|
|
4
16
|
|
|
5
17
|
The Titanium Style Sheets (TSS) file uses a JSON-like syntax to define the attributes of elements in the XML files. All TSS attributes are the properties of the Titanium object.
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Alloy XML Markup
|
|
2
2
|
|
|
3
|
+
<!-- TOC-START -->
|
|
4
|
+
## Contents
|
|
5
|
+
|
|
6
|
+
- [Introduction](#introduction)
|
|
7
|
+
- [Collection Element](#collection-element)
|
|
8
|
+
- [Model Element](#model-element)
|
|
9
|
+
- [Module Attribute](#module-attribute)
|
|
10
|
+
- [Module Element](#module-element)
|
|
11
|
+
- [Require Element](#require-element)
|
|
12
|
+
- [Namespace](#namespace)
|
|
13
|
+
- [Conditional Code](#conditional-code)
|
|
14
|
+
- [Property Mapping](#property-mapping)
|
|
15
|
+
- [Event Handling](#event-handling)
|
|
16
|
+
- [Data Binding](#data-binding)
|
|
17
|
+
- [Non-Standard Syntax](#non-standard-syntax)
|
|
18
|
+
|
|
19
|
+
<!-- TOC-END -->
|
|
20
|
+
|
|
3
21
|
## Introduction
|
|
4
22
|
|
|
5
23
|
In Alloy, the XML markup abstracts the Titanium SDK UI components, so you do not need to code the creation and setup of these components using JavaScript and the Titanium SDK API. All view files must be placed in the `app/views` folder of your project with the `.xml` file extension.
|
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
Complete examples of common patterns with anti-patterns and correct implementations.
|
|
4
4
|
|
|
5
|
+
<!-- TOC-START -->
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- [Community-Discovered Patterns](#community-discovered-patterns)
|
|
9
|
+
- [Titanium Layout Patterns](#titanium-layout-patterns)
|
|
10
|
+
- [Horizontal Row with Space Between](#horizontal-row-with-space-between)
|
|
11
|
+
- [Centered Content (Vertically and Horizontally)](#centered-content-vertically-and-horizontally)
|
|
12
|
+
- [Vertical Stack with Spacing](#vertical-stack-with-spacing)
|
|
13
|
+
- [Header with Left/Right Elements](#header-with-leftright-elements)
|
|
14
|
+
- [Available Layout Classes](#available-layout-classes)
|
|
15
|
+
- [Manual .tss Files Anti-Pattern](#manual-tss-files-anti-pattern)
|
|
16
|
+
- [Grid with Percentages](#grid-with-percentages)
|
|
17
|
+
- [Gap Usage](#gap-usage)
|
|
18
|
+
- [Padding on Container Views](#padding-on-container-views)
|
|
19
|
+
- [`w-full` vs `w-screen`](#w-full-vs-w-screen)
|
|
20
|
+
- [`rounded-full` Without Size](#rounded-full-without-size)
|
|
21
|
+
- [Square Brackets for Arbitrary Values](#square-brackets-for-arbitrary-values)
|
|
22
|
+
- [Layout Defaults](#layout-defaults)
|
|
23
|
+
- [ScrollView Without `content-w-screen` / `content-h-auto`](#scrollview-without-content-w-screen-content-h-auto)
|
|
24
|
+
- [Deprecated `theme.View.DEFAULT` vs `theme.extend.View`](#deprecated-themeviewdefault-vs-themeextendview)
|
|
25
|
+
- [Quick Reference Table](#quick-reference-table)
|
|
26
|
+
|
|
27
|
+
<!-- TOC-END -->
|
|
28
|
+
|
|
5
29
|
## Community-Discovered Patterns
|
|
6
30
|
|
|
7
31
|
The examples below catalog anti-patterns observed in real Titanium + PurgeTSS projects, paired with the correct approach. They are not exhaustive rules from official docs — they reflect pitfalls the community has hit often enough to warrant dedicated guidance.
|
|
@@ -6,6 +6,18 @@ For core animation methods (play, toggle, apply, open, close, draggable, undragg
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
<!-- TOC-START -->
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Property Inheritance from the Animation Object](#property-inheritance-from-the-animation-object)
|
|
13
|
+
- [Timing and Utility Classes](#timing-and-utility-classes)
|
|
14
|
+
- [Utility Functions](#utility-functions)
|
|
15
|
+
- [Implementation Rules](#implementation-rules)
|
|
16
|
+
- [Method Implementation Template](#method-implementation-template)
|
|
17
|
+
- [Complex UI Example](#complex-ui-example)
|
|
18
|
+
|
|
19
|
+
<!-- TOC-END -->
|
|
20
|
+
|
|
9
21
|
## Property Inheritance from the Animation Object
|
|
10
22
|
|
|
11
23
|
All methods in the Animation module inherit properties from the `<Animation>` object's classes. You configure animation behavior in XML and it applies to every method call.
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# PurgeTSS Animation System
|
|
2
2
|
|
|
3
|
+
<!-- TOC-START -->
|
|
4
|
+
## Contents
|
|
5
|
+
|
|
6
|
+
- [Introduction](#introduction)
|
|
7
|
+
- [Methods Reference (All 15)](#methods-reference-all-15)
|
|
8
|
+
- [Modifiers](#modifiers)
|
|
9
|
+
- [The `play` Method](#the-play-method)
|
|
10
|
+
- [Callback Event Object](#callback-event-object)
|
|
11
|
+
- [The `apply` Method](#the-apply-method)
|
|
12
|
+
- [The `open` and `close` Methods](#the-open-and-close-methods)
|
|
13
|
+
- [The `draggable` Method](#the-draggable-method)
|
|
14
|
+
- [The `undraggable` Method](#the-undraggable-method)
|
|
15
|
+
- [The `detectCollisions` Method](#the-detectcollisions-method)
|
|
16
|
+
- [The `sequence` Method](#the-sequence-method)
|
|
17
|
+
- [The `swap` Method](#the-swap-method)
|
|
18
|
+
- [The `pulse` Method](#the-pulse-method)
|
|
19
|
+
- [The `shake` Method](#the-shake-method)
|
|
20
|
+
- [The `snapTo` Method](#the-snapto-method)
|
|
21
|
+
- [The `reorder` Method](#the-reorder-method)
|
|
22
|
+
- [The `transition` Method](#the-transition-method)
|
|
23
|
+
|
|
24
|
+
<!-- TOC-END -->
|
|
25
|
+
|
|
3
26
|
## Introduction
|
|
4
27
|
|
|
5
28
|
PurgeTSS includes an Animation module for 2D Matrix animations and transformations. It works on single elements, arrays of elements, or individual children of an element.
|
|
@@ -9,6 +9,29 @@ For the terse flag reference, see the [`brand` command reference](./cli-commands
|
|
|
9
9
|
> The `brand` command at a glance
|
|
10
10
|
> `purgetss brand` turns one logo into every asset a Titanium app needs to ship — launcher icons, adaptive icons, iOS 18+ Dark/Tinted variants, marketplace artwork, and optional notification/splash icons. Works on both Alloy and Classic projects.
|
|
11
11
|
|
|
12
|
+
<!-- TOC-START -->
|
|
13
|
+
## Contents
|
|
14
|
+
|
|
15
|
+
- [Quick start](#quick-start)
|
|
16
|
+
- [The `purgetss/brand/` convention](#the-purgetssbrand-convention)
|
|
17
|
+
- [The `brand:` config section (v7.7.0 grouped structure)](#the-brand-config-section-v770-grouped-structure)
|
|
18
|
+
- [Overwrite confirmation](#overwrite-confirmation)
|
|
19
|
+
- [What gets generated](#what-gets-generated)
|
|
20
|
+
- [Android dark mode](#android-dark-mode)
|
|
21
|
+
- [Android 12+ splash artwork](#android-12-splash-artwork)
|
|
22
|
+
- [Android legacy splash fallback](#android-legacy-splash-fallback)
|
|
23
|
+
- [iOS 18+ Dark and Tinted variants](#ios-18-dark-and-tinted-variants)
|
|
24
|
+
- [Google Play Feature Graphic (v7.10.0)](#google-play-feature-graphic-v7100)
|
|
25
|
+
- [Brand color](#brand-color)
|
|
26
|
+
- [Padding guidance](#padding-guidance)
|
|
27
|
+
- [Cleanup legacy branding artifacts](#cleanup-legacy-branding-artifacts)
|
|
28
|
+
- [Troubleshooting](#troubleshooting)
|
|
29
|
+
- [Full flag reference](#full-flag-reference)
|
|
30
|
+
- [Community-Discovered Patterns](#community-discovered-patterns)
|
|
31
|
+
- [See also](#see-also)
|
|
32
|
+
|
|
33
|
+
<!-- TOC-END -->
|
|
34
|
+
|
|
12
35
|
## Quick start
|
|
13
36
|
|
|
14
37
|
Drop a logo file into `purgetss/brand/`, then run the command. That's the whole workflow.
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# The `apply` Directive
|
|
2
2
|
|
|
3
|
+
<!-- TOC-START -->
|
|
4
|
+
## Contents
|
|
5
|
+
|
|
6
|
+
- [Create Complex Classes and IDs](#create-complex-classes-and-ids)
|
|
7
|
+
- [Set Any ID, Class, or Ti Element](#set-any-id-class-or-ti-element)
|
|
8
|
+
- [Use Default Classes](#use-default-classes)
|
|
9
|
+
- [Use Icon Font Classes (v7.10.0)](#use-icon-font-classes-v7100)
|
|
10
|
+
- [Use Arbitrary Values](#use-arbitrary-values)
|
|
11
|
+
- [Use Newly Defined Classes in `config.cjs`](#use-newly-defined-classes-in-configcjs)
|
|
12
|
+
- [Set a String of Classes or an Array of Classes](#set-a-string-of-classes-or-an-array-of-classes)
|
|
13
|
+
- [Combine with Platform, Device, or Conditional Blocks](#combine-with-platform-device-or-conditional-blocks)
|
|
14
|
+
- [Customizing Window, View, and ImageView](#customizing-window-view-and-imageview)
|
|
15
|
+
- [Platform-Specific Classes](#platform-specific-classes)
|
|
16
|
+
- [Community-Discovered Patterns](#community-discovered-patterns)
|
|
17
|
+
|
|
18
|
+
<!-- TOC-END -->
|
|
19
|
+
|
|
3
20
|
## Create Complex Classes and IDs
|
|
4
21
|
|
|
5
22
|
> **INFO**
|
|
@@ -7,6 +7,19 @@ When you need a one-off value that is not in the defaults, use arbitrary values
|
|
|
7
7
|
>
|
|
8
8
|
> Square bracket notation (`[10px]`) is **not supported** because Titanium handles platform and conditional statements in `.tss` files differently. Use parentheses (`(10px)`) instead.
|
|
9
9
|
|
|
10
|
+
<!-- TOC-START -->
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- [Class syntax pre-validation](#class-syntax-pre-validation)
|
|
14
|
+
- [Arbitrary nesting depth in `theme` objects (v7.10.0)](#arbitrary-nesting-depth-in-theme-objects-v7100)
|
|
15
|
+
- [Color Properties](#color-properties)
|
|
16
|
+
- [Spacing Properties](#spacing-properties)
|
|
17
|
+
- [Miscellaneous Properties](#miscellaneous-properties)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
- [Community-Discovered Patterns](#community-discovered-patterns)
|
|
20
|
+
|
|
21
|
+
<!-- TOC-END -->
|
|
22
|
+
|
|
10
23
|
## Class syntax pre-validation
|
|
11
24
|
|
|
12
25
|
Starting with PurgeTSS v7.8.0, the build runs a pre-validation pass over class names found in XML views and JS controllers before purging. When it spots a malformed arbitrary-value utility, it stops and prints a structured `Class Syntax Error` block so the offending class can be fixed before any TSS is generated. If multiple errors are present, all of them are reported in the same run.
|
|
@@ -4,6 +4,17 @@ Full A–Z table of every Titanium property and its PurgeTSS class prefix. Split
|
|
|
4
4
|
|
|
5
5
|
> Before suggesting ANY class, verify it exists: `grep -E "PATTERN" ./purgetss/styles/utilities.tss`
|
|
6
6
|
|
|
7
|
+
<!-- TOC-START -->
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [All 416 Titanium Properties with Classes](#all-416-titanium-properties-with-classes)
|
|
11
|
+
- [A-E](#a-e)
|
|
12
|
+
- [F-L](#f-l)
|
|
13
|
+
- [M-P](#m-p)
|
|
14
|
+
- [R-Z](#r-z)
|
|
15
|
+
|
|
16
|
+
<!-- TOC-END -->
|
|
17
|
+
|
|
7
18
|
## All 416 Titanium Properties with Classes
|
|
8
19
|
|
|
9
20
|
The following properties have PurgeTSS utility classes. Each property name converts to kebab-case for the class prefix.
|