@maccesar/titools 2.2.8 → 2.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/ti-pro.md +17 -17
- package/lib/commands/update.js +86 -50
- package/lib/downloader.js +17 -2
- package/package.json +1 -1
package/agents/ti-pro.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ti-pro
|
|
3
|
-
description: Titanium SDK and Alloy research specialist.
|
|
3
|
+
description: Titanium SDK and Alloy research specialist. Loads the 7 Titanium skills and uses them together to answer research-style questions. Use for codebase analysis, architecture reviews, Titanium SDK implementation research, cross-feature questions, and platform-specific differences. Returns concrete findings with file and line references.
|
|
4
4
|
skills:
|
|
5
5
|
- alloy-guides
|
|
6
6
|
- alloy-howtos
|
|
@@ -15,27 +15,27 @@ model: sonnet
|
|
|
15
15
|
|
|
16
16
|
# Titanium SDK Research Specialist
|
|
17
17
|
|
|
18
|
-
You are a
|
|
18
|
+
You are a research specialist for Titanium SDK and Alloy mobile development. With more than 15 year of experience.
|
|
19
19
|
|
|
20
20
|
## What You Do
|
|
21
21
|
|
|
22
|
-
When invoked, you research complex topics by consulting
|
|
22
|
+
When invoked, you research complex topics by consulting all 7 Titanium skills:
|
|
23
23
|
|
|
24
24
|
- `ti-expert` - Architecture, patterns, conventions
|
|
25
25
|
- `alloy-guides` - Alloy MVC complete reference
|
|
26
26
|
- `alloy-howtos` - CLI, config, debugging
|
|
27
|
-
- `purgetss` - Utility-first styling classes (optional
|
|
27
|
+
- `purgetss` - Utility-first styling classes (optional add-on; use when the project already has it or the user asks about it)
|
|
28
28
|
- `ti-guides` - SDK official guides, Hyperloop, distribution
|
|
29
29
|
- `ti-howtos` - Native features, platform-specific APIs
|
|
30
30
|
- `ti-ui` - UI/UX patterns, layouts, gestures
|
|
31
31
|
|
|
32
32
|
## Research Process
|
|
33
33
|
|
|
34
|
-
1. **Understand the query** -
|
|
35
|
-
2. **Consult
|
|
36
|
-
3. **Cross-reference** -
|
|
37
|
-
4. **Provide specifics** - Include file paths, line numbers, code examples
|
|
38
|
-
5. **Cite sources** -
|
|
34
|
+
1. **Understand the query** - Identify what needs research.
|
|
35
|
+
2. **Consult all relevant skills** - Use the full set.
|
|
36
|
+
3. **Cross-reference** - Connect findings across skills.
|
|
37
|
+
4. **Provide specifics** - Include file paths, line numbers, and code examples.
|
|
38
|
+
5. **Cite sources** - Note which skill and file each answer comes from.
|
|
39
39
|
|
|
40
40
|
## What You're Good For
|
|
41
41
|
|
|
@@ -47,7 +47,7 @@ When invoked, you research complex topics by consulting **all 7 preloaded titani
|
|
|
47
47
|
| **Architecture review** | "Review this project's folder structure and service layer" |
|
|
48
48
|
| **Platform differences** | "Research iOS vs Android differences for this feature" |
|
|
49
49
|
|
|
50
|
-
## What You're
|
|
50
|
+
## What You're Not For
|
|
51
51
|
|
|
52
52
|
| Use Instead | Reason |
|
|
53
53
|
| -------------------------------------------- | -------------------------------------- |
|
|
@@ -60,11 +60,11 @@ When invoked, you research complex topics by consulting **all 7 preloaded titani
|
|
|
60
60
|
|
|
61
61
|
When returning research findings:
|
|
62
62
|
|
|
63
|
-
1. **Summary** - Brief overview of findings
|
|
64
|
-
2. **Key Points** - Bulleted list with specific references
|
|
65
|
-
3. **Code Examples** - From the skills, with source citations
|
|
66
|
-
4. **Related Skills** - Which skills were consulted
|
|
67
|
-
5. **File References** - Specific `path:line` format
|
|
63
|
+
1. **Summary** - Brief overview of findings.
|
|
64
|
+
2. **Key Points** - Bulleted list with specific references.
|
|
65
|
+
3. **Code Examples** - From the skills, with source citations.
|
|
66
|
+
4. **Related Skills** - Which skills were consulted.
|
|
67
|
+
5. **File References** - Specific `path:line` format.
|
|
68
68
|
|
|
69
69
|
Example:
|
|
70
70
|
```markdown
|
|
@@ -81,7 +81,7 @@ Your app uses `Ti.App.fireEvent` which causes memory leaks.
|
|
|
81
81
|
|
|
82
82
|
## Tool Usage
|
|
83
83
|
|
|
84
|
-
You have
|
|
84
|
+
You have read-only tools: `Read`, `Grep`, `Glob`, `Bash`.
|
|
85
85
|
|
|
86
86
|
Use them to:
|
|
87
87
|
- Search the codebase when asked to analyze it
|
|
@@ -89,7 +89,7 @@ Use them to:
|
|
|
89
89
|
- Verify claims against actual code
|
|
90
90
|
- List files/directories and inspect structure with shell commands
|
|
91
91
|
|
|
92
|
-
You
|
|
92
|
+
You cannot modify files. If the user asks for changes, provide the research and suggest using the appropriate skill or main conversation for implementation.
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
package/lib/commands/update.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import { agentsCommand } from './agents.js';
|
|
24
24
|
import {
|
|
25
25
|
checkForUpdate,
|
|
26
|
-
|
|
26
|
+
fetchLatestNpmVersion,
|
|
27
27
|
} from '../downloader.js';
|
|
28
28
|
import { createSkillSymlinks } from '../symlink.js';
|
|
29
29
|
import { formatList, isTitaniumProject } from '../utils.js';
|
|
@@ -42,6 +42,49 @@ function hasAnySkillSymlink(platformSkillsDir) {
|
|
|
42
42
|
return skillList.some((skill) => existsSync(join(platformSkillsDir, skill)));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Perform the actual update for a specific scope
|
|
47
|
+
* @param {string|undefined} baseDir - Base directory (undefined = global, path = local)
|
|
48
|
+
* @param {string} repoDir - Repository directory
|
|
49
|
+
* @param {Object} spinner - Ora spinner instance
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
*/
|
|
52
|
+
async function performUpdate(baseDir, repoDir, spinner) {
|
|
53
|
+
// Detect platforms with existing symlinks (only update those)
|
|
54
|
+
const detectedPlatforms = detectPlatforms(baseDir);
|
|
55
|
+
const platformsWithSymlinks = detectedPlatforms.filter((p) =>
|
|
56
|
+
hasAnySkillSymlink(p.skillsDir)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Install skills
|
|
60
|
+
spinner.start('Syncing skills...');
|
|
61
|
+
const skillsResult = await installSkills(repoDir, baseDir);
|
|
62
|
+
spinner.succeed(`${skillsResult.installed.length} skills updated`);
|
|
63
|
+
|
|
64
|
+
// Install agents (only if Claude Code has symlinks)
|
|
65
|
+
const claudePlatform = platformsWithSymlinks.find((p) => p.name === 'claude');
|
|
66
|
+
if (claudePlatform) {
|
|
67
|
+
spinner.start('Syncing agents...');
|
|
68
|
+
const agentsResult = await installAgents(repoDir, baseDir);
|
|
69
|
+
if (agentsResult.installed.length > 0) {
|
|
70
|
+
spinner.succeed('Platform agents updated');
|
|
71
|
+
} else {
|
|
72
|
+
spinner.info('No agents to sync');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
cleanupLegacyArtifacts(baseDir);
|
|
77
|
+
|
|
78
|
+
// Update symlinks silently for platforms that already had them
|
|
79
|
+
for (const platform of platformsWithSymlinks) {
|
|
80
|
+
await createSkillSymlinks(
|
|
81
|
+
platform.skillsDir,
|
|
82
|
+
SKILLS,
|
|
83
|
+
baseDir
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
45
88
|
/**
|
|
46
89
|
* Update command handler
|
|
47
90
|
* @param {Object} options - Command options
|
|
@@ -65,15 +108,16 @@ export async function updateCommand(options) {
|
|
|
65
108
|
const localPlatforms = detectPlatforms(projectDir);
|
|
66
109
|
const hasLocalSkills = isProject && hasSkillsAt(projectDir);
|
|
67
110
|
const hasGlobalSkills = hasSkillsAt(undefined);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
111
|
+
|
|
112
|
+
// Only show prompt if BOTH local and global skills exist
|
|
113
|
+
if (hasLocalSkills && hasGlobalSkills) {
|
|
71
114
|
try {
|
|
72
115
|
const scope = await select({
|
|
73
|
-
message: '
|
|
116
|
+
message: 'Both local and global skills detected. What do you want to update:',
|
|
74
117
|
choices: [
|
|
75
118
|
{ name: 'Global skills (user home)', value: 'global' },
|
|
76
119
|
{ name: 'Local skills (current project)', value: 'local' },
|
|
120
|
+
{ name: 'Both locations', value: 'both' },
|
|
77
121
|
],
|
|
78
122
|
theme: {
|
|
79
123
|
style: {
|
|
@@ -87,32 +131,44 @@ export async function updateCommand(options) {
|
|
|
87
131
|
process.exit(0);
|
|
88
132
|
}
|
|
89
133
|
if (scope === 'local') {
|
|
90
|
-
baseDir =
|
|
134
|
+
baseDir = projectDir;
|
|
135
|
+
} else if (scope === 'both') {
|
|
136
|
+
baseDir = 'both';
|
|
91
137
|
}
|
|
92
138
|
} catch (error) {
|
|
93
139
|
console.log('\nCancelled.');
|
|
94
140
|
process.exit(0);
|
|
95
141
|
}
|
|
142
|
+
} else if (hasLocalSkills && !hasGlobalSkills) {
|
|
143
|
+
// Only local skills exist, update local
|
|
144
|
+
baseDir = projectDir;
|
|
96
145
|
}
|
|
146
|
+
// If only global skills exist, baseDir remains undefined (global)
|
|
97
147
|
}
|
|
98
148
|
|
|
99
|
-
|
|
149
|
+
// Display mode
|
|
150
|
+
if (baseDir === 'both') {
|
|
151
|
+
console.log(chalk.cyan('Mode: Updating both global and local skills'));
|
|
152
|
+
} else if (baseDir) {
|
|
100
153
|
console.log(chalk.cyan('Mode: Local update (current project)'));
|
|
101
154
|
} else {
|
|
102
155
|
console.log(chalk.cyan('Mode: Global update (user home)'));
|
|
103
156
|
}
|
|
104
157
|
console.log('');
|
|
105
158
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
// Verify skills are installed
|
|
160
|
+
if (baseDir !== 'both') {
|
|
161
|
+
const skillsDir = getAgentsSkillsDir(baseDir);
|
|
162
|
+
const hasSkillsInstalled = skillsDir && SKILLS.some((skill) => existsSync(join(skillsDir, skill)));
|
|
163
|
+
if (!hasSkillsInstalled) {
|
|
164
|
+
console.log(chalk.yellow('No skills installed at this location.'));
|
|
165
|
+
console.log('Install them first with:');
|
|
166
|
+
console.log(' titools install');
|
|
167
|
+
console.log('');
|
|
168
|
+
console.log('Looked for skills in:');
|
|
169
|
+
console.log(` ${baseDir ? 'Local' : 'Global'}: ${skillsDir}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
116
172
|
}
|
|
117
173
|
|
|
118
174
|
// Check for updates
|
|
@@ -121,11 +177,11 @@ export async function updateCommand(options) {
|
|
|
121
177
|
try {
|
|
122
178
|
const hasUpdate = await checkForUpdate(PACKAGE_VERSION);
|
|
123
179
|
|
|
124
|
-
// If there's a newer version on
|
|
180
|
+
// If there's a newer version on npm, prompt user to update CLI first
|
|
125
181
|
if (hasUpdate) {
|
|
126
182
|
let latestVersion = '(newer)';
|
|
127
183
|
try {
|
|
128
|
-
latestVersion = await
|
|
184
|
+
latestVersion = await fetchLatestNpmVersion();
|
|
129
185
|
} catch {
|
|
130
186
|
// Ignore error, we already know there's an update
|
|
131
187
|
}
|
|
@@ -158,38 +214,18 @@ export async function updateCommand(options) {
|
|
|
158
214
|
return;
|
|
159
215
|
}
|
|
160
216
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// Install skills
|
|
168
|
-
spinner.start('Syncing skills...');
|
|
169
|
-
const skillsResult = await installSkills(repoDir, baseDir);
|
|
170
|
-
spinner.succeed(`${skillsResult.installed.length} skills updated`);
|
|
171
|
-
|
|
172
|
-
// Install agents (only if Claude Code has symlinks)
|
|
173
|
-
const claudePlatform = platformsWithSymlinks.find((p) => p.name === 'claude');
|
|
174
|
-
if (claudePlatform) {
|
|
175
|
-
spinner.start('Syncing agents...');
|
|
176
|
-
const agentsResult = await installAgents(repoDir, baseDir);
|
|
177
|
-
if (agentsResult.installed.length > 0) {
|
|
178
|
-
spinner.succeed('Platform agents updated');
|
|
179
|
-
} else {
|
|
180
|
-
spinner.info('No agents to sync');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
cleanupLegacyArtifacts(baseDir);
|
|
217
|
+
// Perform update(s)
|
|
218
|
+
if (baseDir === 'both') {
|
|
219
|
+
// Update both global and local
|
|
220
|
+
console.log(chalk.bold('Updating global skills...'));
|
|
221
|
+
await performUpdate(undefined, repoDir, spinner);
|
|
222
|
+
console.log('');
|
|
185
223
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
baseDir
|
|
192
|
-
);
|
|
224
|
+
console.log(chalk.bold('Updating local skills...'));
|
|
225
|
+
await performUpdate(process.cwd(), repoDir, spinner);
|
|
226
|
+
} else {
|
|
227
|
+
// Update single scope
|
|
228
|
+
await performUpdate(baseDir, repoDir, spinner);
|
|
193
229
|
}
|
|
194
230
|
|
|
195
231
|
// Summary
|
package/lib/downloader.js
CHANGED
|
@@ -112,13 +112,28 @@ export async function downloadRawFile(filePath, destPath, ref = 'main') {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* Fetch latest version from npm registry
|
|
116
|
+
* @returns {Promise<string>} Latest version number
|
|
117
|
+
*/
|
|
118
|
+
export async function fetchLatestNpmVersion() {
|
|
119
|
+
const response = await fetch('https://registry.npmjs.org/@maccesar/titools');
|
|
120
|
+
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw new Error(`Failed to fetch npm info: ${response.statusText}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const data = await response.json();
|
|
126
|
+
return data['dist-tags'].latest;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if an update is available (checks npm)
|
|
116
131
|
* @param {string} currentVersion - Current version
|
|
117
132
|
* @returns {Promise<boolean>} True if update available
|
|
118
133
|
*/
|
|
119
134
|
export async function checkForUpdate(currentVersion) {
|
|
120
135
|
try {
|
|
121
|
-
const latestVersion = await
|
|
136
|
+
const latestVersion = await fetchLatestNpmVersion();
|
|
122
137
|
// Remove 'v' prefix if present
|
|
123
138
|
const latest = latestVersion.replace(/^v/, '');
|
|
124
139
|
const current = currentVersion.replace(/^v/, '');
|