@maccesar/titools 2.0.7 → 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 +17 -17
  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,204 @@
1
+ import {
2
+ Separator,
3
+ ValidationError,
4
+ createPrompt,
5
+ isBackspaceKey,
6
+ isDownKey,
7
+ isEnterKey,
8
+ isNumberKey,
9
+ isUpKey,
10
+ makeTheme,
11
+ useEffect,
12
+ useKeypress,
13
+ useMemo,
14
+ usePagination,
15
+ usePrefix,
16
+ useRef,
17
+ useState,
18
+ } from '@inquirer/core';
19
+ import { cursorHide } from '@inquirer/ansi';
20
+ import { styleText } from 'node:util';
21
+ import figures from '@inquirer/figures';
22
+
23
+ const selectTheme = {
24
+ icon: { cursor: figures.pointer },
25
+ style: {
26
+ disabled: (text) => styleText('dim', `- ${text}`),
27
+ description: (text) => styleText('cyan', text),
28
+ keysHelpTip: (keys) =>
29
+ keys
30
+ .map(([key, action]) => `${styleText('bold', key)} ${styleText('dim', action)}`)
31
+ .join(styleText('dim', ' • ')),
32
+ },
33
+ indexMode: 'hidden',
34
+ keybindings: [],
35
+ };
36
+
37
+ function isSelectable(item) {
38
+ return !Separator.isSeparator(item) && !item.disabled;
39
+ }
40
+
41
+ function normalizeChoices(choices) {
42
+ return choices.map((choice) => {
43
+ if (Separator.isSeparator(choice)) return choice;
44
+ if (typeof choice !== 'object' || choice === null || !('value' in choice)) {
45
+ const name = String(choice);
46
+ return {
47
+ value: choice,
48
+ name,
49
+ short: name,
50
+ disabled: false,
51
+ };
52
+ }
53
+ const name = choice.name ?? String(choice.value);
54
+ const normalizedChoice = {
55
+ value: choice.value,
56
+ name,
57
+ short: choice.short ?? name,
58
+ disabled: choice.disabled ?? false,
59
+ };
60
+ if (choice.description) {
61
+ normalizedChoice.description = choice.description;
62
+ }
63
+ return normalizedChoice;
64
+ });
65
+ }
66
+
67
+ const selectCancel = createPrompt((config, done) => {
68
+ const { loop = true, pageSize = 7, cancelValue = 'cancel' } = config;
69
+ const theme = makeTheme(selectTheme, config.theme);
70
+ const { keybindings } = theme;
71
+ const [status, setStatus] = useState('idle');
72
+ const [cancelled, setCancelled] = useState(false);
73
+ const prefix = usePrefix({ status, theme });
74
+ const searchTimeoutRef = useRef();
75
+ const searchEnabled = !keybindings.includes('vim');
76
+ const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
77
+ const bounds = useMemo(() => {
78
+ const first = items.findIndex(isSelectable);
79
+ const last = items.findLastIndex(isSelectable);
80
+ if (first === -1) {
81
+ throw new ValidationError('[select prompt] No selectable choices. All choices are disabled.');
82
+ }
83
+ return { first, last };
84
+ }, [items]);
85
+ const defaultItemIndex = useMemo(() => {
86
+ if (!('default' in config)) return -1;
87
+ return items.findIndex((item) => isSelectable(item) && item.value === config.default);
88
+ }, [config.default, items]);
89
+ const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
90
+ const selectedChoice = items[active];
91
+
92
+ useKeypress((key, rl) => {
93
+ clearTimeout(searchTimeoutRef.current);
94
+ if (key.name?.toLowerCase() === 'q') {
95
+ setCancelled(true);
96
+ setStatus('done');
97
+ done(cancelValue);
98
+ } else if (isEnterKey(key)) {
99
+ setStatus('done');
100
+ done(selectedChoice.value);
101
+ }
102
+ else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
103
+ rl.clearLine(0);
104
+ if (
105
+ loop ||
106
+ (isUpKey(key, keybindings) && active !== bounds.first) ||
107
+ (isDownKey(key, keybindings) && active !== bounds.last)
108
+ ) {
109
+ const offset = isUpKey(key, keybindings) ? -1 : 1;
110
+ let next = active;
111
+ do {
112
+ next = (next + offset + items.length) % items.length;
113
+ } while (!isSelectable(items[next]));
114
+ setActive(next);
115
+ }
116
+ } else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
117
+ const selectedIndex = Number(rl.line) - 1;
118
+ let selectableIndex = -1;
119
+ const position = items.findIndex((item) => {
120
+ if (Separator.isSeparator(item)) return false;
121
+ selectableIndex++;
122
+ return selectableIndex === selectedIndex;
123
+ });
124
+ const item = items[position];
125
+ if (item != null && isSelectable(item)) {
126
+ setActive(position);
127
+ }
128
+ searchTimeoutRef.current = setTimeout(() => {
129
+ rl.clearLine(0);
130
+ }, 700);
131
+ } else if (isBackspaceKey(key)) {
132
+ rl.clearLine(0);
133
+ } else if (searchEnabled) {
134
+ const searchTerm = rl.line.toLowerCase();
135
+ const matchIndex = items.findIndex((item) => {
136
+ if (Separator.isSeparator(item) || !isSelectable(item)) return false;
137
+ return item.name.toLowerCase().startsWith(searchTerm);
138
+ });
139
+ if (matchIndex !== -1) {
140
+ setActive(matchIndex);
141
+ }
142
+ searchTimeoutRef.current = setTimeout(() => {
143
+ rl.clearLine(0);
144
+ }, 700);
145
+ }
146
+ });
147
+
148
+ useEffect(() => () => {
149
+ clearTimeout(searchTimeoutRef.current);
150
+ }, []);
151
+
152
+ const message = theme.style.message(config.message, status);
153
+ const helpLine = theme.style.keysHelpTip([
154
+ ['↑↓', 'navigate'],
155
+ ['⏎', 'select'],
156
+ ['q', 'quit'],
157
+ ]);
158
+ let separatorCount = 0;
159
+ const page = usePagination({
160
+ items,
161
+ active,
162
+ renderItem({ item, isActive, index }) {
163
+ if (Separator.isSeparator(item)) {
164
+ separatorCount++;
165
+ return ` ${item.separator}`;
166
+ }
167
+ const indexLabel = theme.indexMode === 'number' ? `${index + 1 - separatorCount}. ` : '';
168
+ if (item.disabled) {
169
+ const disabledLabel = typeof item.disabled === 'string' ? item.disabled : '(disabled)';
170
+ return theme.style.disabled(`${indexLabel}${item.name} ${disabledLabel}`);
171
+ }
172
+ const color = isActive ? theme.style.highlight : (x) => x;
173
+ const cursor = isActive ? theme.icon.cursor : ` `;
174
+ return color(`${cursor} ${indexLabel}${item.name}`);
175
+ },
176
+ pageSize,
177
+ loop,
178
+ });
179
+
180
+ if (status === 'done') {
181
+ if (cancelled) {
182
+ return [prefix, message].filter(Boolean).join(' ');
183
+ }
184
+ return [prefix, message, theme.style.answer(selectedChoice.short)]
185
+ .filter(Boolean)
186
+ .join(' ');
187
+ }
188
+
189
+ const { description } = selectedChoice;
190
+ const lines = [
191
+ [prefix, message].filter(Boolean).join(' '),
192
+ page,
193
+ ' ',
194
+ description ? theme.style.description(description) : '',
195
+ helpLine,
196
+ ]
197
+ .filter(Boolean)
198
+ .join('\n')
199
+ .trimEnd();
200
+ return `${lines}${cursorHide}`;
201
+ });
202
+
203
+ export { Separator };
204
+ export default selectCancel;
package/lib/symlink.js CHANGED
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { mkdirSync, existsSync } from 'fs';
7
- import { symlink, readlink, unlink } from 'fs/promises';
8
- import { join, dirname } from 'path';
7
+ import { symlink, readlink, unlink, lstat } from 'fs/promises';
8
+ import { join, dirname, relative } from 'path';
9
9
  import { copy, remove } from 'fs-extra';
10
10
  import { isWindows } from './platform.js';
11
11
 
@@ -13,9 +13,10 @@ import { isWindows } from './platform.js';
13
13
  * Create a symlink or copy as fallback
14
14
  * @param {string} target - Target path (what the symlink points to)
15
15
  * @param {string} path - Symlink path (where to create it)
16
+ * @param {boolean} useRelative - Whether to use a relative path for the symlink
16
17
  * @returns {Promise<boolean>} True if successful
17
18
  */
18
- export async function createSymlinkOrCopy(target, path) {
19
+ export async function createSymlinkOrCopy(target, path, useRelative = false) {
19
20
  // Ensure parent directory exists
20
21
  const dir = dirname(path);
21
22
  if (!existsSync(dir)) {
@@ -27,9 +28,16 @@ export async function createSymlinkOrCopy(target, path) {
27
28
  await removePath(path);
28
29
  }
29
30
 
31
+ // Determine final target path
32
+ let finalTarget = target;
33
+ if (useRelative) {
34
+ finalTarget = relative(dirname(path), target);
35
+ }
36
+
30
37
  // Try creating symlink first
31
38
  try {
32
- await symlink(target, path, 'dir');
39
+ // Windows requires 'junction' or 'dir' for directory symlinks
40
+ await symlink(finalTarget, path, 'dir');
33
41
  return true;
34
42
  } catch (error) {
35
43
  // On Windows or if symlink fails, copy the directory
@@ -64,11 +72,12 @@ async function removePath(path) {
64
72
  * Create symlinks for all skills to a platform directory
65
73
  * @param {string} platformSkillsDir - Platform skills directory
66
74
  * @param {Array} skills - List of skill names
75
+ * @param {string} baseDir - Optional base directory for target resolution
67
76
  * @returns {Promise<Object>} Results object with success/failure counts
68
77
  */
69
- export async function createSkillSymlinks(platformSkillsDir, skills) {
78
+ export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
70
79
  const { getAgentsSkillsDir } = await import('./config.js');
71
- const agentsSkillsDir = getAgentsSkillsDir();
80
+ const agentsSkillsDir = getAgentsSkillsDir(baseDir);
72
81
 
73
82
  const results = {
74
83
  linked: [],
@@ -80,11 +89,14 @@ export async function createSkillSymlinks(platformSkillsDir, skills) {
80
89
  mkdirSync(platformSkillsDir, { recursive: true });
81
90
  }
82
91
 
92
+ // Use relative symlinks if we are in a local installation
93
+ const useRelative = !!baseDir;
94
+
83
95
  for (const skill of skills) {
84
96
  const target = join(agentsSkillsDir, skill);
85
97
  const linkPath = join(platformSkillsDir, skill);
86
98
 
87
- if (await createSymlinkOrCopy(target, linkPath)) {
99
+ if (await createSymlinkOrCopy(target, linkPath, useRelative)) {
88
100
  results.linked.push(skill);
89
101
  } else {
90
102
  results.failed.push(skill);
package/lib/utils.js CHANGED
@@ -40,20 +40,21 @@ export function removeOldBlock(filePath) {
40
40
  }
41
41
 
42
42
  const content = readFileSync(filePath, 'utf8');
43
- // Regex to match any Titanium knowledge block (old or new)
44
- const regex = /[\s\S]*?<!-- TITANIUM-KNOWLEDGE-(v[\d.]+|START) -->[\s\S]*?<!-- (END-TITANIUM-KNOWLEDGE|TITANIUM-KNOWLEDGE-END) -->[\s\S]*?\n*/g;
43
+ // Surgical regex to match ONLY the Titanium knowledge block
44
+ const blockRegex = /\n*<!-- TITANIUM-KNOWLEDGE-(v[\d.]+|START) -->[\s\S]*?<!-- (END-TITANIUM-KNOWLEDGE|TITANIUM-KNOWLEDGE-END) -->\n*/g;
45
45
 
46
- const newContent = content.replace(regex, '\n\n');
46
+ const newContent = content.replace(blockRegex, '\n').trimEnd();
47
47
 
48
- writeFileSync(filePath, newContent, 'utf8');
48
+ writeFileSync(filePath, newContent + '\n', 'utf8');
49
49
  return true;
50
50
  }
51
51
 
52
52
  /**
53
53
  * Build the compressed documentation index dynamically by scanning skills
54
+ * @param {string} rootPath - The root path to use in the index (default: ~/.agents/skills)
54
55
  * @returns {string} The compressed index section
55
56
  */
56
- export function buildKnowledgeIndex() {
57
+ export function buildKnowledgeIndex(rootPath = '~/.agents/skills') {
57
58
  let skills = [];
58
59
 
59
60
  try {
@@ -84,18 +85,19 @@ export function buildKnowledgeIndex() {
84
85
  // Note: Using \u0060 to avoid backtick parsing issues in template literals
85
86
  return '### Compressed Documentation Index\n\n' +
86
87
  '\u0060\u0060\u0060\n' +
87
- '[Titanium SDK Docs Index]|root: ~/.agents/skills\n' +
88
+ `[Titanium SDK Docs Index]|root: ${rootPath}\n` +
88
89
  '|' + indexEntries.join('\n|') + '\n' +
89
90
  '\u0060\u0060\u0060';
90
91
  }
91
92
 
92
93
  /**
93
94
  * Create the Titanium knowledge block content dynamically
95
+ * @param {string} rootPath - The root path to use in the index
94
96
  * @returns {string} The knowledge block content
95
97
  */
96
- export function createKnowledgeBlock() {
98
+ export function createKnowledgeBlock(rootPath) {
97
99
  // Build the compressed index dynamically
98
- const compressedIndex = buildKnowledgeIndex();
100
+ const compressedIndex = buildKnowledgeIndex(rootPath);
99
101
 
100
102
  // Build the knowledge block using template literals
101
103
  const blockHeader = `${BLOCK_START}\n<!-- Version: ${TITANIUM_KNOWLEDGE_VERSION} -->`;
@@ -108,27 +110,28 @@ Always consult the documentation files below rather than relying on training dat
108
110
 
109
111
  This knowledge index is based on the latest Titanium SDK documentation.`;
110
112
 
111
- return `\n\n${blockHeader}\n${commentContent}\n\n${compressedIndex}\n${BLOCK_END}\n`;
113
+ return `\n${blockHeader}\n${commentContent}\n\n${compressedIndex}\n${BLOCK_END}\n`;
112
114
  }
113
115
 
114
116
  /**
115
117
  * Add or update Titanium knowledge block in a file
116
118
  * @param {string} filePath - Path to the file
119
+ * @param {string} rootPath - Optional root path for the index
117
120
  * @returns {boolean} True if file was updated
118
121
  */
119
- export function addOrUpdateBlock(filePath) {
122
+ export function addOrUpdateBlock(filePath, rootPath) {
120
123
  let content = '';
121
124
 
122
125
  if (existsSync(filePath)) {
123
126
  content = readFileSync(filePath, 'utf8');
124
127
  }
125
128
 
126
- const block = createKnowledgeBlock();
129
+ const block = createKnowledgeBlock(rootPath);
127
130
 
128
131
  // Regex to find ALL existing blocks (old versioned or new static)
129
132
  // Global flag /g is CRITICAL here to remove every single occurrence
130
133
  const globalBlockRegex = /<!-- TITANIUM-KNOWLEDGE-(v[\d.]+|START) -->[\s\S]*?<!-- (END-TITANIUM-KNOWLEDGE|TITANIUM-KNOWLEDGE-END) -->/g;
131
-
134
+
132
135
  // Clean up all existing blocks to prevent accumulation
133
136
  content = content.replace(globalBlockRegex, '').trim();
134
137
 
@@ -216,11 +219,8 @@ export function formatList(items) {
216
219
  * @returns {Array} Array of version parts
217
220
  */
218
221
  export function parseVersion(version) {
219
- return version
220
- .replace('.GA', '')
221
- .replace('.RC', '-')
222
- .split(/[.-]/)
223
- .map((v) => parseInt(v, 10) || 0);
222
+ const matches = version.match(/\d+/g) || [];
223
+ return matches.map((v) => parseInt(v, 10));
224
224
  }
225
225
 
226
226
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/titools",
3
- "version": "2.0.7",
3
+ "version": "2.2.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",
@@ -41,23 +41,23 @@
41
41
  "node": ">=18.0.0"
42
42
  },
43
43
  "dependencies": {
44
- "chalk": "^5.3.0",
45
- "commander": "^12.0.0",
46
- "fs-extra": "^11.2.0",
47
- "inquirer": "^9.2.0",
48
- "ora": "^8.0.0",
49
- "tar": "^7.4.3"
44
+ "@inquirer/prompts": "^8.2.0",
45
+ "chalk": "^5.6.2",
46
+ "commander": "^14.0.3",
47
+ "fs-extra": "^11.3.3",
48
+ "ora": "^9.1.0",
49
+ "tar": "^7.5.7"
50
50
  },
51
51
  "devDependencies": {
52
- "eslint": "^8.57.0",
53
- "prettier": "^3.2.0"
52
+ "eslint": "^9.39.2",
53
+ "prettier": "^3.8.1"
54
54
  },
55
55
  "files": [
56
56
  "bin/",
57
57
  "lib/",
58
58
  "skills/",
59
59
  "agents/",
60
- "AGENTS-TEMPLATE.md",
60
+ "AGENTS-VERCEL-RESEARCH.md",
61
61
  "scripts/titools-docs"
62
62
  ],
63
63
  "publishConfig": {
@@ -26,28 +26,6 @@ This skill automatically detects Alloy projects when invoked and provides framew
26
26
  - **Not detected** → Indicates this skill is for Alloy projects only, does not suggest Alloy-specific features
27
27
  :::
28
28
 
29
- ## Table of Contents
30
-
31
- - [Alloy MVC Framework Guide](#alloy-mvc-framework-guide)
32
- - [Project Detection](#project-detection)
33
- - [Table of Contents](#table-of-contents)
34
- - [Quick Reference](#quick-reference)
35
- - [Project Structure](#project-structure)
36
- - [MVC Quick Start](#mvc-quick-start)
37
- - [Key Concepts](#key-concepts)
38
- - [Critical Rules](#critical-rules)
39
- - [Platform-Specific Properties in TSS](#platform-specific-properties-in-tss)
40
- - [Common Patterns](#common-patterns)
41
- - [Creating a Model](#creating-a-model)
42
- - [Data Binding](#data-binding)
43
- - [Platform-Specific Code](#platform-specific-code)
44
- - [Widget Usage](#widget-usage)
45
- - [Compilation Process](#compilation-process)
46
- - [References](#references)
47
- - [Related Skills](#related-skills)
48
-
49
- ---
50
-
51
29
  ## Quick Reference
52
30
 
53
31
  | Topic | Reference File |
@@ -61,7 +39,7 @@ This skill automatically detects Alloy projects when invoked and provides framew
61
39
  | Controllers-less views, patterns | [VIEWS_WITHOUT_CONTROLLERS.md](references/VIEWS_WITHOUT_CONTROLLERS.md) |
62
40
  | Creating and using widgets | [WIDGETS.md](references/WIDGETS.md) |
63
41
  | CLI commands, code generation | [CLI_TASKS.md](references/CLI_TASKS.md) |
64
- | PurgeTSS integration and features | [PURGETSS.md](references/PURGETSS.md) |
42
+ | PurgeTSS integration (optional addon) | [PURGETSS.md](references/PURGETSS.md) |
65
43
 
66
44
  ## Project Structure
67
45
 
@@ -156,7 +134,7 @@ Using `Ti.UI.iOS.*` or `Ti.UI.Android.*` properties in TSS WITHOUT platform modi
156
134
 
157
135
  **Available modifiers:** `[platform=ios]`, `[platform=android]`, `[formFactor=handheld]`, `[formFactor=tablet]`, `[if=Alloy.Globals.customVar]`
158
136
 
159
- **For more platform-specific patterns, see** [Platform Modifiers (purgetss)](skills/purgetss/references/platform-modifiers.md) or [Platform UI guides (ti-ui)](skills/ti-ui/references/platform-ui-ios.md).
137
+ **For more platform-specific patterns, see** [Code Conventions (ti-expert)](skills/ti-expert/references/code-conventions.md#platform--device-modifiers) or [Platform UI guides (ti-ui)](skills/ti-ui/references/platform-ui-ios.md).
160
138
  :::
161
139
 
162
140
  ## Common Patterns
@@ -206,9 +184,9 @@ Read detailed documentation from the reference files listed above based on your
206
184
 
207
185
  For tasks beyond Alloy MVC basics, use these complementary skills:
208
186
 
209
- | Task | Use This Skill |
210
- | ----------------------------------------- | -------------- |
211
- | Modern architecture, services, patterns | `alloy-expert` |
212
- | Alloy CLI, config files, debugging errors | `alloy-howtos` |
213
- | Utility-first styling with PurgeTSS | `purgetss` |
214
- | Native features (location, push, media) | `ti-howtos` |
187
+ | Task | Use This Skill |
188
+ | ---------------------------------------------- | -------------- |
189
+ | Modern architecture, services, patterns | `ti-expert` |
190
+ | Alloy CLI, config files, debugging errors | `alloy-howtos` |
191
+ | Utility-first styling with PurgeTSS (optional) | `purgetss` |
192
+ | Native features (location, push, media) | `ti-howtos` |
@@ -1,24 +1,6 @@
1
1
  # Alloy Tasks with the CLI
2
2
 
3
- ## Table of Contents
4
-
5
- - [Alloy Tasks with the CLI](#alloy-tasks-with-the-cli)
6
- - [Table of Contents](#table-of-contents)
7
- - [Creating a New Application](#creating-a-new-application)
8
- - [Creating a New Application Using a Test Application](#creating-a-new-application-using-a-test-application)
9
- - [Generating Components](#generating-components)
10
- - [Generating a Controller](#generating-a-controller)
11
- - [Generating a View](#generating-a-view)
12
- - [Generating a Style](#generating-a-style)
13
- - [Generating a Model](#generating-a-model)
14
- - [Generating a Migration](#generating-a-migration)
15
- - [Generating a Widget](#generating-a-widget)
16
- - [Generating alloy.jmk](#generating-alloyjmk)
17
- - [Extracting Localization Strings](#extracting-localization-strings)
18
- - [Compiling a Specific View-Controller](#compiling-a-specific-view-controller)
19
- - [Building an Application](#building-an-application)
20
- - [Installing Special Project Components](#installing-special-project-components)
21
- - [Installing the Compiler Plugin](#installing-the-compiler-plugin)
3
+ Alloy provides command-line interface (CLI) tasks to create a new project, generate skeleton components such as controllers and models, and compile the source files into the Titanium project.
22
4
 
23
5
  ## Creating a New Application
24
6
 
@@ -88,7 +70,7 @@ To generate a style for a view-controller:
88
70
  alloy generate style <name> [--widgetname <widget_name>]
89
71
  ```
90
72
 
91
- Alloy uses the id and attribute names in the markup file to populate the skeleton style file. This creates `app/styles/<name>.tss`.
73
+ Alloy uses the id and attribute names in the markup file to populate the skeleton style file. This creates `app/styles/<name>.tss`. If you add new `id` or `class` attributes to the markup file, running either of these commands updates the style file with the new attributes.
92
74
 
93
75
  To generate style files for all view-controllers:
94
76
 
@@ -104,7 +86,7 @@ To generate a model:
104
86
  alloy generate model <name> <adapter> [<col_name_1>:<col_type_1> <col_name_2>:<col_type_2> ...] [-o path_to_project/app]
105
87
  ```
106
88
 
107
- The fourth parameter selects the adapter type: `sql` for SQLite or `properties` for local storage.
89
+ The fourth parameter selects the adapter type: `sql` for SQLite or `properties` for local storage. The fifth parameter defines the table schema. This is required for `sql` and `properties` adapter types.
108
90
 
109
91
  This creates `app/models/<name>.js`, and `app/migrations/DATETIME_<name>.js` if the adapter type is 'sql'.
110
92
 
@@ -148,8 +130,8 @@ alloy extract-i18n [language] [--apply]
148
130
 
149
131
  **Parameters**:
150
132
 
151
- * `language` – Optional. Two-letter language code (`en` or `es`). Default is `en`.
152
- * `--apply` – Optional. Writes new entries to `strings.xml`. Without it, displays a preview.
133
+ * `language` – Optional. Two-letter language code (`en`, `es`, etc.). Default is `en`.
134
+ * `--apply` – Optional. Writes new entries to `strings.xml`. Without it, displays a preview. This is a safe operation: it only adds new entries and never removes existing ones.
153
135
 
154
136
  Supported functions:
155
137
 
@@ -197,7 +179,15 @@ Running with `--apply`:
197
179
  alloy extract-i18n --apply
198
180
  ```
199
181
 
200
- Generates `app/i18n/en/strings.xml`:
182
+ Generates `app/i18n/en/strings.xml`.
183
+
184
+ To generate strings for another language, pass the language code:
185
+
186
+ ```bash
187
+ alloy extract-i18n es --apply
188
+ ```
189
+
190
+ This generates `app/i18n/es/strings.xml`.
201
191
 
202
192
  ```xml
203
193
  <resources>
@@ -1,26 +1,5 @@
1
1
  # Alloy Concepts
2
2
 
3
- ## Table of Contents
4
-
5
- - [Alloy Concepts](#alloy-concepts)
6
- - [Table of Contents](#table-of-contents)
7
- - [Overview](#overview)
8
- - [Model-View-Controller](#model-view-controller)
9
- - [Alloy: MVC with Backbone](#alloy-mvc-with-backbone)
10
- - [Alloy and the Titanium SDK](#alloy-and-the-titanium-sdk)
11
- - [Titanium SDK to Alloy Mapping](#titanium-sdk-to-alloy-mapping)
12
- - [Convention over Configuration](#convention-over-configuration)
13
- - [Platform-Specific Resources](#platform-specific-resources)
14
- - [Widgets](#widgets)
15
- - [Builtins](#builtins)
16
- - [Compilation Process](#compilation-process)
17
- - [Cleanup](#cleanup)
18
- - [Build Configuration](#build-configuration)
19
- - [Alloy Framework, Assets, and Lib](#alloy-framework-assets-and-lib)
20
- - [Model-View-Controller and Widget Generation](#model-view-controller-and-widget-generation)
21
- - [Main Application](#main-application)
22
- - [Code Optimization](#code-optimization)
23
-
24
3
  ## Overview
25
4
 
26
5
  This guide covers the important concepts related to the Alloy framework, including the model-view-controller framework, convention-over-configuration design, widgets, and built-in support from Backbone.js and Underscore.js.
@@ -59,14 +38,15 @@ For assets, such as images, any references to the `Resources` folder in the Tita
59
38
 
60
39
  | Titanium SDK Component | Alloy Component |
61
40
  | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
62
- | **Titanium.UI.* Objects** | **XML element**. Remove the namespace. For some elements, you may need to assign the `ns` attribute. |
41
+ | **Titanium.UI.\* Objects** (and others: `Ti.Android.Menu`, `Ti.Facebook.LoginButton`, `Ti.Map`, `Ti.Media.VideoPlayer`) | **XML element**. Remove the namespace. For some elements, you may need to assign the `ns` attribute. Some objects are implicitly assigned namespaces by Alloy. |
63
42
  | `Titanium.UI.createButton();` | `<Button />` creates a button |
64
43
  | | **TSS element**. Remove the namespace. |
65
44
  | | `"Button":{ /* Button attributes */ }` |
66
45
  | **Titanium Object properties** | **XML attribute** if the property can be expressed as a string, number or Titanium SDK constant. |
67
46
  | `Titanium.UI.createButton({text: "Foobar", top: 0, width: Ti.UI.SIZE});` | `<Button title="Foobar" top="0" width="Ti.UI.SIZE"/>` |
68
- | | **TSS attribute** if the property can be directly expressed as a string, number, Titanium SDK constant, dictionary or array. |
47
+ | | **TSS attribute** if the property can be directly expressed as a string, number, Titanium SDK constant, dictionary or array. Indirectly, you can assign a method or value to the `Alloy.Globals` or `Alloy.CFG` namespace and reference it in the TSS file. |
69
48
  | | `"Button":{ title: "Foobar", top: 0, width: Ti.UI.SIZE }` |
49
+ | **Titanium Object properties (proxy)** | Some properties that take Titanium objects can be expressed inline using XML tags (Property Mapping). See [VIEWS_XML.md](./VIEWS_XML.md#property-mapping). |
70
50
  | **Titanium Object methods** | Use in the controller code. You need to define the `id` attribute of the object in the XML markup, so the object can be referenced in the controller. |
71
51
  | `const button = Titanium.UI.createButton(); button.setTitle('Push Me!');` | `// Need to give the object an ID: <Button id="button" />` then `$.button.setTitle('Push Me!');` |
72
52
  | **Titanium Object events** | XML attribute to bind a callback in the associated controller. Capitalize the first character of the event name and append 'on' to the beginning of the name. |
@@ -84,7 +64,7 @@ The following is a list of directories and files that can be found in an Alloy p
84
64
  | `app/alloy.jmk` | Build configuration file. See Build Configuration File (alloy.jmk). |
85
65
  | `app/alloy.js` | Initializer file used to preconfigure components or override Alloy methods before the main controller is executed. |
86
66
  | `app/config.json` | Project configuration file. See Project Configuration File (config.json). |
87
- | `app/assets` | Contains image assets and other files that need to be copied into the `Resources` directory. Reference these files in the code without the 'app/assets' path. |
67
+ | `app/assets` | Contains image assets and other files that need to be copied into the `Resources` directory. Reference these files in the code without the 'app/assets' path (and without the platform-specific folder if it is inside one). |
88
68
  | `app/controllers` | Contains controllers in the format `filename.js` to a corresponding view file `app/views/filename.xml`. |
89
69
  | `app/i18n` | Since Alloy 1.8.0 and Titanium 5.2.0 Language Strings are sourced from `app/i18n` and Alloy will generate the `i18n` folder in the project root. |
90
70
  | `app/lib` | Contains application-specific library code, typically in the CommonJS format. |
@@ -96,7 +76,7 @@ The following is a list of directories and files that can be found in an Alloy p
96
76
  | `app/themes` | Contains themes to customize the assets and styles of the entire GUI. |
97
77
  | `app/views` | Contains views in the format `filename.xml` with the optional corresponding files `app/controllers/filename.js` and `app/styles/filename.tss`. |
98
78
  | `app/widgets` | Contains widget files. Each widget will have its own `app`-like directory structure. |
99
- | `i18n` | Since Alloy 1.8.0 and Titanium 5.2.0 Language Strings are sourced from `app/i18n` and Alloy will generate the `i18n` folder in the project root. |
79
+ | `i18n` | Since Alloy 1.8.0 and Titanium 5.2.0 Language Strings are sourced from `app/i18n` and Alloy will generate the `i18n` folder in the project root. Before Alloy 1.8.0, the `i18n` folder was managed in the project root directly. |
100
80
  | `Resources` | Contains the Titanium files generated by the Alloy interface from the `app` directory. All files will be overwritten each time the application is built. Since Alloy 1.3.0, Alloy creates a separate Titanium project for each platform you build for in the `Resources/<platform>` folder. |
101
81
 
102
82
  **Notes:** the `lib`, `migrations`, `themes` and `widgets` folders are not automatically generated when creating a new project. The `migrations` and `widgets` folder will be generated by the Alloy command-line interface if any of those components are generated. The `lib` and `themes` folders will need to be manually created.
@@ -1,22 +1,5 @@
1
1
  # Alloy Controllers
2
2
 
3
- ## Table of Contents
4
-
5
- - [Alloy Controllers](#alloy-controllers)
6
- - [Table of Contents](#table-of-contents)
7
- - [Overview](#overview)
8
- - [Controllers](#controllers)
9
- - [Events](#events)
10
- - [Inheritance](#inheritance)
11
- - [Conditional Code](#conditional-code)
12
- - [Passing Arguments](#passing-arguments)
13
- - [Global Namespace](#global-namespace)
14
- - [Initializer File (alloy.js)](#initializer-file-alloyjs)
15
- - [Library Code and CommonJS Modules](#library-code-and-commonjs-modules)
16
- - [Specs Folder](#specs-folder)
17
- - [Platform-Specific Library Folders](#platform-specific-library-folders)
18
- - [Extending Alloy, Underscore.js and Backbone.js](#extending-alloy-underscorejs-and-backbonejs)
19
-
20
3
  ## Overview
21
4
 
22
5
  This topic covers how to write controller code as well as other JavaScript files excluding models. Some traditional Titanium development is required, since Alloy controllers make direct calls to the Titanium SDK API to manipulate UI objects and access non-UI APIs.
@@ -132,8 +115,8 @@ The following are the constants defined by Alloy for use in the controller code:
132
115
  * `ENV_DEV` : true if the current compiler target is built for development (running in the simulator or emulator)
133
116
  * `ENV_TEST` : true if the current compiler target is built for testing on a device
134
117
  * `ENV_PRODUCTION` : true if the current compiler target is built for production (running after a packaged installation)
135
- * `DIST_ADHOC` (since Alloy 1.4.0) : true if the current compiler target is built for iOS Ad Hoc distribution
136
- * `DIST_STORE` (since Alloy 1.4.0) : true if the current compiler target is built for deployment to the Google Play Store or iTunes App Store
118
+ * `DIST_ADHOC` (since Alloy 1.4.0) : true if the current compiler target is built for iOS Ad Hoc distribution, for example, if you set the `-T dist-adhoc` option when building with the Titanium CLI. Note that `ENV_PRODUCTION` will be true too since this deployment is only for production builds.
119
+ * `DIST_STORE` (since Alloy 1.4.0) : true if the current compiler target is built for deployment to the Google Play Store or iTunes App Store, for example, if you set the `-T dist-appstore` or `-T dist-playstore` option. Note that `ENV_PRODUCTION` will be true too since this deployment is only for production builds.
137
120
 
138
121
  For example, since iOS devices do not include a back button, the application can conditionally add one to a window controller:
139
122