ui-pro-forge-cli 2.5.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 (188) hide show
  1. package/README.md +99 -0
  2. package/assets/data/app-interface.csv +31 -0
  3. package/assets/data/charts.csv +26 -0
  4. package/assets/data/colors.csv +194 -0
  5. package/assets/data/google-fonts.csv +1924 -0
  6. package/assets/data/icons.csv +106 -0
  7. package/assets/data/landing.csv +35 -0
  8. package/assets/data/motion.csv +17 -0
  9. package/assets/data/products.csv +193 -0
  10. package/assets/data/react-performance.csv +45 -0
  11. package/assets/data/stacks/angular.csv +51 -0
  12. package/assets/data/stacks/astro.csv +54 -0
  13. package/assets/data/stacks/avalonia.csv +57 -0
  14. package/assets/data/stacks/flutter.csv +53 -0
  15. package/assets/data/stacks/html-tailwind.csv +56 -0
  16. package/assets/data/stacks/javafx.csv +76 -0
  17. package/assets/data/stacks/jetpack-compose.csv +53 -0
  18. package/assets/data/stacks/laravel.csv +51 -0
  19. package/assets/data/stacks/nextjs.csv +53 -0
  20. package/assets/data/stacks/nuxt-ui.csv +71 -0
  21. package/assets/data/stacks/nuxtjs.csv +59 -0
  22. package/assets/data/stacks/react-native.csv +52 -0
  23. package/assets/data/stacks/react.csv +54 -0
  24. package/assets/data/stacks/shadcn.csv +61 -0
  25. package/assets/data/stacks/solidjs.csv +21 -0
  26. package/assets/data/stacks/svelte.csv +54 -0
  27. package/assets/data/stacks/swiftui.csv +51 -0
  28. package/assets/data/stacks/threejs.csv +54 -0
  29. package/assets/data/stacks/uno.csv +60 -0
  30. package/assets/data/stacks/vue.csv +50 -0
  31. package/assets/data/stacks/winui.csv +60 -0
  32. package/assets/data/styles.csv +86 -0
  33. package/assets/data/typography.csv +75 -0
  34. package/assets/data/ui-reasoning.csv +162 -0
  35. package/assets/data/ux-guidelines.csv +100 -0
  36. package/assets/scripts/core.py +463 -0
  37. package/assets/scripts/design_system.py +1371 -0
  38. package/assets/scripts/search.py +162 -0
  39. package/assets/scripts/tests/test_core.py +134 -0
  40. package/assets/scripts/validate_data.py +114 -0
  41. package/assets/skills/banner-design/SKILL.md +196 -0
  42. package/assets/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
  43. package/assets/skills/brand/SKILL.md +97 -0
  44. package/assets/skills/brand/references/approval-checklist.md +169 -0
  45. package/assets/skills/brand/references/asset-organization.md +157 -0
  46. package/assets/skills/brand/references/brand-guideline-template.md +140 -0
  47. package/assets/skills/brand/references/color-palette-management.md +186 -0
  48. package/assets/skills/brand/references/consistency-checklist.md +94 -0
  49. package/assets/skills/brand/references/logo-usage-rules.md +185 -0
  50. package/assets/skills/brand/references/messaging-framework.md +85 -0
  51. package/assets/skills/brand/references/typography-specifications.md +214 -0
  52. package/assets/skills/brand/references/update.md +118 -0
  53. package/assets/skills/brand/references/visual-identity.md +96 -0
  54. package/assets/skills/brand/references/voice-framework.md +88 -0
  55. package/assets/skills/brand/scripts/extract-colors.cjs +341 -0
  56. package/assets/skills/brand/scripts/inject-brand-context.cjs +349 -0
  57. package/assets/skills/brand/scripts/sync-brand-to-tokens.cjs +248 -0
  58. package/assets/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +52 -0
  59. package/assets/skills/brand/scripts/validate-asset.cjs +387 -0
  60. package/assets/skills/brand/templates/brand-guidelines-starter.md +275 -0
  61. package/assets/skills/design/SKILL.md +313 -0
  62. package/assets/skills/design/data/cip/deliverables.csv +51 -0
  63. package/assets/skills/design/data/cip/industries.csv +21 -0
  64. package/assets/skills/design/data/cip/mockup-contexts.csv +21 -0
  65. package/assets/skills/design/data/cip/styles.csv +21 -0
  66. package/assets/skills/design/data/icon/styles.csv +16 -0
  67. package/assets/skills/design/data/logo/colors.csv +56 -0
  68. package/assets/skills/design/data/logo/industries.csv +56 -0
  69. package/assets/skills/design/data/logo/styles.csv +56 -0
  70. package/assets/skills/design/references/banner-sizes-and-styles.md +118 -0
  71. package/assets/skills/design/references/cip-deliverable-guide.md +95 -0
  72. package/assets/skills/design/references/cip-design.md +121 -0
  73. package/assets/skills/design/references/cip-prompt-engineering.md +84 -0
  74. package/assets/skills/design/references/cip-style-guide.md +68 -0
  75. package/assets/skills/design/references/design-routing.md +207 -0
  76. package/assets/skills/design/references/icon-design.md +122 -0
  77. package/assets/skills/design/references/logo-color-psychology.md +101 -0
  78. package/assets/skills/design/references/logo-design.md +92 -0
  79. package/assets/skills/design/references/logo-prompt-engineering.md +158 -0
  80. package/assets/skills/design/references/logo-style-guide.md +109 -0
  81. package/assets/skills/design/references/slides-copywriting-formulas.md +84 -0
  82. package/assets/skills/design/references/slides-create.md +4 -0
  83. package/assets/skills/design/references/slides-html-template.md +295 -0
  84. package/assets/skills/design/references/slides-layout-patterns.md +137 -0
  85. package/assets/skills/design/references/slides-strategies.md +94 -0
  86. package/assets/skills/design/references/slides.md +42 -0
  87. package/assets/skills/design/references/social-photos-design.md +329 -0
  88. package/assets/skills/design/scripts/cip/core.py +215 -0
  89. package/assets/skills/design/scripts/cip/generate.py +484 -0
  90. package/assets/skills/design/scripts/cip/render-html.py +424 -0
  91. package/assets/skills/design/scripts/cip/search.py +127 -0
  92. package/assets/skills/design/scripts/icon/generate.py +487 -0
  93. package/assets/skills/design/scripts/logo/core.py +175 -0
  94. package/assets/skills/design/scripts/logo/generate.py +362 -0
  95. package/assets/skills/design/scripts/logo/search.py +114 -0
  96. package/assets/skills/design-system/SKILL.md +244 -0
  97. package/assets/skills/design-system/data/slide-backgrounds.csv +11 -0
  98. package/assets/skills/design-system/data/slide-charts.csv +26 -0
  99. package/assets/skills/design-system/data/slide-color-logic.csv +14 -0
  100. package/assets/skills/design-system/data/slide-copy.csv +26 -0
  101. package/assets/skills/design-system/data/slide-layout-logic.csv +16 -0
  102. package/assets/skills/design-system/data/slide-layouts.csv +26 -0
  103. package/assets/skills/design-system/data/slide-strategies.csv +16 -0
  104. package/assets/skills/design-system/data/slide-typography.csv +15 -0
  105. package/assets/skills/design-system/references/component-specs.md +236 -0
  106. package/assets/skills/design-system/references/component-tokens.md +214 -0
  107. package/assets/skills/design-system/references/primitive-tokens.md +203 -0
  108. package/assets/skills/design-system/references/semantic-tokens.md +215 -0
  109. package/assets/skills/design-system/references/states-and-variants.md +241 -0
  110. package/assets/skills/design-system/references/tailwind-integration.md +251 -0
  111. package/assets/skills/design-system/references/token-architecture.md +224 -0
  112. package/assets/skills/design-system/scripts/embed-tokens.cjs +99 -0
  113. package/assets/skills/design-system/scripts/fetch-background.py +317 -0
  114. package/assets/skills/design-system/scripts/generate-slide.py +770 -0
  115. package/assets/skills/design-system/scripts/generate-tokens.cjs +205 -0
  116. package/assets/skills/design-system/scripts/html-token-validator.py +327 -0
  117. package/assets/skills/design-system/scripts/search-slides.py +218 -0
  118. package/assets/skills/design-system/scripts/slide-token-validator.py +35 -0
  119. package/assets/skills/design-system/scripts/slide_search_core.py +453 -0
  120. package/assets/skills/design-system/scripts/tests/test_validate_tokens.py +48 -0
  121. package/assets/skills/design-system/scripts/validate-tokens.cjs +246 -0
  122. package/assets/skills/design-system/templates/design-tokens-starter.json +143 -0
  123. package/assets/skills/slides/SKILL.md +40 -0
  124. package/assets/skills/slides/references/copywriting-formulas.md +84 -0
  125. package/assets/skills/slides/references/create.md +4 -0
  126. package/assets/skills/slides/references/html-template.md +295 -0
  127. package/assets/skills/slides/references/layout-patterns.md +137 -0
  128. package/assets/skills/slides/references/slide-strategies.md +94 -0
  129. package/assets/skills/ui-styling/LICENSE.txt +202 -0
  130. package/assets/skills/ui-styling/SKILL.md +324 -0
  131. package/assets/skills/ui-styling/references/canvas-design-system.md +320 -0
  132. package/assets/skills/ui-styling/references/shadcn-accessibility.md +471 -0
  133. package/assets/skills/ui-styling/references/shadcn-components.md +424 -0
  134. package/assets/skills/ui-styling/references/shadcn-theming.md +373 -0
  135. package/assets/skills/ui-styling/references/tailwind-customization.md +483 -0
  136. package/assets/skills/ui-styling/references/tailwind-responsive.md +382 -0
  137. package/assets/skills/ui-styling/references/tailwind-utilities.md +455 -0
  138. package/assets/skills/ui-styling/scripts/requirements.txt +17 -0
  139. package/assets/skills/ui-styling/scripts/shadcn_add.py +308 -0
  140. package/assets/skills/ui-styling/scripts/tailwind_config_gen.py +473 -0
  141. package/assets/skills/ui-styling/scripts/tests/coverage-ui.json +1 -0
  142. package/assets/skills/ui-styling/scripts/tests/requirements.txt +3 -0
  143. package/assets/skills/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
  144. package/assets/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +394 -0
  145. package/assets/templates/base/quick-reference.md +297 -0
  146. package/assets/templates/base/skill-content.md +384 -0
  147. package/assets/templates/platforms/agent.json +21 -0
  148. package/assets/templates/platforms/augment.json +18 -0
  149. package/assets/templates/platforms/claude.json +21 -0
  150. package/assets/templates/platforms/codebuddy.json +21 -0
  151. package/assets/templates/platforms/codewhale.json +21 -0
  152. package/assets/templates/platforms/codex.json +21 -0
  153. package/assets/templates/platforms/continue.json +21 -0
  154. package/assets/templates/platforms/copilot.json +21 -0
  155. package/assets/templates/platforms/cursor.json +21 -0
  156. package/assets/templates/platforms/droid.json +21 -0
  157. package/assets/templates/platforms/gemini.json +21 -0
  158. package/assets/templates/platforms/kilocode.json +21 -0
  159. package/assets/templates/platforms/kiro.json +21 -0
  160. package/assets/templates/platforms/opencode.json +21 -0
  161. package/assets/templates/platforms/qoder.json +21 -0
  162. package/assets/templates/platforms/roocode.json +21 -0
  163. package/assets/templates/platforms/trae.json +21 -0
  164. package/assets/templates/platforms/warp.json +18 -0
  165. package/assets/templates/platforms/windsurf.json +21 -0
  166. package/dist/commands/init.d.ts +11 -0
  167. package/dist/commands/init.js +165 -0
  168. package/dist/commands/uninstall.d.ts +7 -0
  169. package/dist/commands/uninstall.js +131 -0
  170. package/dist/commands/update.d.ts +7 -0
  171. package/dist/commands/update.js +73 -0
  172. package/dist/commands/versions.d.ts +5 -0
  173. package/dist/commands/versions.js +36 -0
  174. package/dist/index.d.ts +2 -0
  175. package/dist/index.js +78 -0
  176. package/dist/types/index.d.ts +40 -0
  177. package/dist/types/index.js +25 -0
  178. package/dist/utils/detect.d.ts +8 -0
  179. package/dist/utils/detect.js +115 -0
  180. package/dist/utils/extract.d.ts +15 -0
  181. package/dist/utils/extract.js +125 -0
  182. package/dist/utils/github.d.ts +12 -0
  183. package/dist/utils/github.js +97 -0
  184. package/dist/utils/logger.d.ts +8 -0
  185. package/dist/utils/logger.js +9 -0
  186. package/dist/utils/template.d.ts +50 -0
  187. package/dist/utils/template.js +249 -0
  188. package/package.json +65 -0
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { initCommand } from './commands/init.js';
7
+ import { versionsCommand } from './commands/versions.js';
8
+ import { updateCommand } from './commands/update.js';
9
+ import { uninstallCommand } from './commands/uninstall.js';
10
+ import { AI_TYPES } from './types/index.js';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
14
+ const program = new Command();
15
+ program
16
+ .name('uipro')
17
+ .description('CLI to install UI Pro Forge skill for AI coding assistants')
18
+ .version(pkg.version);
19
+ program
20
+ .command('init')
21
+ .description('Install UI Pro Forge skill to current project')
22
+ .option('-a, --ai <type>', `AI assistant type (${AI_TYPES.join(', ')})`)
23
+ .option('-f, --force', 'Overwrite existing files')
24
+ .option('-o, --offline', 'Compatibility flag; template installs use bundled assets')
25
+ .option('-g, --global', 'Install globally to home directory (~/) instead of current project')
26
+ .option('-t, --token <token>', 'GitHub Personal Access Token for higher API rate limits')
27
+ .action(async (options) => {
28
+ if (options.ai && !AI_TYPES.includes(options.ai)) {
29
+ console.error(`Invalid AI type: ${options.ai}`);
30
+ console.error(`Valid types: ${AI_TYPES.join(', ')}`);
31
+ process.exit(1);
32
+ }
33
+ await initCommand({
34
+ ai: options.ai,
35
+ force: options.force,
36
+ offline: options.offline,
37
+ global: options.global,
38
+ token: options.token,
39
+ });
40
+ });
41
+ program
42
+ .command('versions')
43
+ .description('List available versions')
44
+ .option('-t, --token <token>', 'GitHub Personal Access Token for higher API rate limits')
45
+ .action(versionsCommand);
46
+ program
47
+ .command('update')
48
+ .description('Update UI Pro Forge to latest version')
49
+ .option('-a, --ai <type>', `AI assistant type (${AI_TYPES.join(', ')})`)
50
+ .option('-t, --token <token>', 'GitHub Personal Access Token for higher API rate limits')
51
+ .action(async (options) => {
52
+ if (options.ai && !AI_TYPES.includes(options.ai)) {
53
+ console.error(`Invalid AI type: ${options.ai}`);
54
+ console.error(`Valid types: ${AI_TYPES.join(', ')}`);
55
+ process.exit(1);
56
+ }
57
+ await updateCommand({
58
+ ai: options.ai,
59
+ token: options.token,
60
+ });
61
+ });
62
+ program
63
+ .command('uninstall')
64
+ .description('Remove UI Pro Forge skill from current project or globally')
65
+ .option('-a, --ai <type>', `AI assistant type (${AI_TYPES.join(', ')})`)
66
+ .option('-g, --global', 'Uninstall from home directory (~/) instead of current project')
67
+ .action(async (options) => {
68
+ if (options.ai && !AI_TYPES.includes(options.ai)) {
69
+ console.error(`Invalid AI type: ${options.ai}`);
70
+ console.error(`Valid types: ${AI_TYPES.join(', ')}`);
71
+ process.exit(1);
72
+ }
73
+ await uninstallCommand({
74
+ ai: options.ai,
75
+ global: options.global,
76
+ });
77
+ });
78
+ program.parse();
@@ -0,0 +1,40 @@
1
+ export type AIType = 'claude' | 'cursor' | 'windsurf' | 'antigravity' | 'copilot' | 'kiro' | 'roocode' | 'codex' | 'qoder' | 'gemini' | 'trae' | 'opencode' | 'continue' | 'codebuddy' | 'droid' | 'kilocode' | 'warp' | 'augment' | 'codewhale' | 'all';
2
+ export type ConcreteAIType = Exclude<AIType, 'all'>;
3
+ export type InstallType = 'full' | 'reference';
4
+ export interface Release {
5
+ tag_name: string;
6
+ name: string;
7
+ published_at: string;
8
+ html_url: string;
9
+ assets: Asset[];
10
+ }
11
+ export interface Asset {
12
+ name: string;
13
+ browser_download_url: string;
14
+ size: number;
15
+ }
16
+ export interface InstallConfig {
17
+ aiType: AIType;
18
+ version?: string;
19
+ force?: boolean;
20
+ }
21
+ export interface PlatformConfig {
22
+ platform: string;
23
+ displayName: string;
24
+ installType: InstallType;
25
+ folderStructure: {
26
+ root: string;
27
+ skillPath: string;
28
+ filename: string;
29
+ };
30
+ scriptPath: string;
31
+ frontmatter: Record<string, string> | null;
32
+ sections: {
33
+ quickReference: boolean;
34
+ };
35
+ title: string;
36
+ description: string;
37
+ skillOrWorkflow: string;
38
+ }
39
+ export declare const AI_TYPES: AIType[];
40
+ export declare const AI_FOLDERS: Record<ConcreteAIType, string[]>;
@@ -0,0 +1,25 @@
1
+ export const AI_TYPES = ['claude', 'cursor', 'windsurf', 'antigravity', 'copilot', 'roocode', 'kiro', 'codex', 'qoder', 'gemini', 'trae', 'opencode', 'continue', 'codebuddy', 'droid', 'kilocode', 'warp', 'augment', 'codewhale', 'all'];
2
+ // Legacy folder mapping for backward compatibility with ZIP-based installs.
3
+ // Note: .shared is included for platforms that used ZIP installs. Post-ZIP platforms
4
+ // (kilocode, warp, augment) include .shared as a no-op for consistent uninstall behavior.
5
+ export const AI_FOLDERS = {
6
+ claude: ['.claude'],
7
+ cursor: ['.cursor', '.shared'],
8
+ windsurf: ['.windsurf', '.shared'],
9
+ antigravity: ['.agents', '.shared'],
10
+ copilot: ['.github', '.shared'],
11
+ kiro: ['.kiro', '.shared'],
12
+ codex: ['.codex'],
13
+ roocode: ['.roo', '.shared'],
14
+ qoder: ['.qoder', '.shared'],
15
+ gemini: ['.gemini', '.shared'],
16
+ trae: ['.trae', '.shared'],
17
+ opencode: ['.opencode', '.shared'],
18
+ continue: ['.continue'],
19
+ codebuddy: ['.codebuddy'],
20
+ droid: ['.factory'],
21
+ kilocode: ['.kilocode', '.shared'],
22
+ warp: ['.warp', '.shared'],
23
+ augment: ['.augment', '.shared'],
24
+ codewhale: ['.codewhale', '.shared'],
25
+ };
@@ -0,0 +1,8 @@
1
+ import type { AIType, ConcreteAIType } from '../types/index.js';
2
+ interface DetectionResult {
3
+ detected: ConcreteAIType[];
4
+ suggested: AIType | null;
5
+ }
6
+ export declare function detectAIType(cwd?: string): DetectionResult;
7
+ export declare function getAITypeDescription(aiType: AIType): string;
8
+ export {};
@@ -0,0 +1,115 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export function detectAIType(cwd = process.cwd()) {
4
+ const detected = [];
5
+ if (existsSync(join(cwd, '.claude'))) {
6
+ detected.push('claude');
7
+ }
8
+ if (existsSync(join(cwd, '.cursor'))) {
9
+ detected.push('cursor');
10
+ }
11
+ if (existsSync(join(cwd, '.windsurf'))) {
12
+ detected.push('windsurf');
13
+ }
14
+ if (existsSync(join(cwd, '.agents')) || existsSync(join(cwd, '.agent'))) {
15
+ detected.push('antigravity');
16
+ }
17
+ if (existsSync(join(cwd, '.github'))) {
18
+ detected.push('copilot');
19
+ }
20
+ if (existsSync(join(cwd, '.kiro'))) {
21
+ detected.push('kiro');
22
+ }
23
+ if (existsSync(join(cwd, '.codex'))) {
24
+ detected.push('codex');
25
+ }
26
+ if (existsSync(join(cwd, '.roo'))) {
27
+ detected.push('roocode');
28
+ }
29
+ if (existsSync(join(cwd, '.qoder'))) {
30
+ detected.push('qoder');
31
+ }
32
+ if (existsSync(join(cwd, '.gemini'))) {
33
+ detected.push('gemini');
34
+ }
35
+ if (existsSync(join(cwd, '.trae'))) {
36
+ detected.push('trae');
37
+ }
38
+ if (existsSync(join(cwd, '.opencode'))) {
39
+ detected.push('opencode');
40
+ }
41
+ if (existsSync(join(cwd, '.continue'))) {
42
+ detected.push('continue');
43
+ }
44
+ if (existsSync(join(cwd, '.codebuddy'))) {
45
+ detected.push('codebuddy');
46
+ }
47
+ if (existsSync(join(cwd, '.factory'))) {
48
+ detected.push('droid');
49
+ }
50
+ if (existsSync(join(cwd, '.kilocode'))) {
51
+ detected.push('kilocode');
52
+ }
53
+ if (existsSync(join(cwd, '.warp'))) {
54
+ detected.push('warp');
55
+ }
56
+ if (existsSync(join(cwd, '.augment'))) {
57
+ detected.push('augment');
58
+ }
59
+ if (existsSync(join(cwd, '.codewhale'))) {
60
+ detected.push('codewhale');
61
+ }
62
+ // Suggest based on what's detected
63
+ let suggested = null;
64
+ if (detected.length === 1) {
65
+ suggested = detected[0];
66
+ }
67
+ else if (detected.length > 1) {
68
+ suggested = 'all';
69
+ }
70
+ return { detected, suggested };
71
+ }
72
+ export function getAITypeDescription(aiType) {
73
+ switch (aiType) {
74
+ case 'claude':
75
+ return 'Claude Code (.claude/skills/)';
76
+ case 'cursor':
77
+ return 'Cursor (.cursor/skills/)';
78
+ case 'windsurf':
79
+ return 'Windsurf (.windsurf/skills/)';
80
+ case 'antigravity':
81
+ return 'Antigravity (.agents/skills/)';
82
+ case 'copilot':
83
+ return 'GitHub Copilot (.github/prompts/)';
84
+ case 'kiro':
85
+ return 'Kiro (.kiro/steering/)';
86
+ case 'codex':
87
+ return 'Codex (.codex/skills/)';
88
+ case 'roocode':
89
+ return 'RooCode (.roo/skills/)';
90
+ case 'qoder':
91
+ return 'Qoder (.qoder/skills/)';
92
+ case 'gemini':
93
+ return 'Gemini CLI (.gemini/skills/)';
94
+ case 'trae':
95
+ return 'Trae (.trae/skills/)';
96
+ case 'opencode':
97
+ return 'OpenCode (.opencode/skills/)';
98
+ case 'continue':
99
+ return 'Continue (.continue/skills/)';
100
+ case 'codebuddy':
101
+ return 'CodeBuddy (.codebuddy/skills/)';
102
+ case 'droid':
103
+ return 'Droid (Factory) (.factory/skills/)';
104
+ case 'kilocode':
105
+ return 'KiloCode (.kilocode/skills/)';
106
+ case 'warp':
107
+ return 'Warp (.warp/skills/)';
108
+ case 'augment':
109
+ return 'Augment (.augment/skills/)';
110
+ case 'codewhale':
111
+ return 'CodeWhale (.codewhale/skills/)';
112
+ case 'all':
113
+ return 'All AI assistants';
114
+ }
115
+ }
@@ -0,0 +1,15 @@
1
+ import type { AIType } from '../types/index.js';
2
+ export declare function extractZip(zipPath: string, destDir: string): Promise<void>;
3
+ export declare function copyFolders(sourceDir: string, targetDir: string, aiType: AIType): Promise<string[]>;
4
+ export declare function cleanup(tempDir: string): Promise<void>;
5
+ /**
6
+ * Create a temporary directory for extracting ZIP files
7
+ */
8
+ export declare function createTempDir(): Promise<string>;
9
+ /**
10
+ * Install from a downloaded and extracted ZIP file
11
+ */
12
+ export declare function installFromZip(zipPath: string, targetDir: string, aiType: AIType): Promise<{
13
+ copiedFolders: string[];
14
+ tempDir: string;
15
+ }>;
@@ -0,0 +1,125 @@
1
+ import { mkdir, rm, access, cp, mkdtemp, readdir } from 'node:fs/promises';
2
+ import { join, basename } from 'node:path';
3
+ import { exec } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { tmpdir } from 'node:os';
6
+ import { AI_FOLDERS } from '../types/index.js';
7
+ const execAsync = promisify(exec);
8
+ const EXCLUDED_FILES = ['settings.local.json'];
9
+ export async function extractZip(zipPath, destDir) {
10
+ try {
11
+ const isWindows = process.platform === 'win32';
12
+ if (isWindows) {
13
+ await execAsync(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`);
14
+ }
15
+ else {
16
+ await execAsync(`unzip -o "${zipPath}" -d "${destDir}"`);
17
+ }
18
+ }
19
+ catch (error) {
20
+ throw new Error(`Failed to extract zip: ${error}`);
21
+ }
22
+ }
23
+ async function exists(path) {
24
+ try {
25
+ await access(path);
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ export async function copyFolders(sourceDir, targetDir, aiType) {
33
+ const copiedFolders = [];
34
+ const foldersToCopy = aiType === 'all'
35
+ ? Object.values(AI_FOLDERS).flat()
36
+ : AI_FOLDERS[aiType];
37
+ // Deduplicate folders (e.g., .shared might be listed multiple times)
38
+ const uniqueFolders = [...new Set(foldersToCopy)];
39
+ for (const folder of uniqueFolders) {
40
+ const sourcePath = join(sourceDir, folder);
41
+ const targetPath = join(targetDir, folder);
42
+ // Check if source folder exists
43
+ const sourceExists = await exists(sourcePath);
44
+ if (!sourceExists) {
45
+ continue;
46
+ }
47
+ // Create target directory if needed
48
+ await mkdir(targetPath, { recursive: true });
49
+ // Filter function to exclude certain files
50
+ const filterFn = (src) => {
51
+ const fileName = basename(src);
52
+ return !EXCLUDED_FILES.includes(fileName);
53
+ };
54
+ // Copy recursively
55
+ try {
56
+ await cp(sourcePath, targetPath, { recursive: true, filter: filterFn });
57
+ copiedFolders.push(folder);
58
+ }
59
+ catch {
60
+ // Try shell fallback for older Node versions
61
+ try {
62
+ if (process.platform === 'win32') {
63
+ await execAsync(`xcopy "${sourcePath}" "${targetPath}" /E /I /Y`);
64
+ }
65
+ else {
66
+ await execAsync(`cp -r "${sourcePath}/." "${targetPath}"`);
67
+ }
68
+ copiedFolders.push(folder);
69
+ }
70
+ catch {
71
+ // Skip if copy fails
72
+ }
73
+ }
74
+ }
75
+ return copiedFolders;
76
+ }
77
+ export async function cleanup(tempDir) {
78
+ try {
79
+ await rm(tempDir, { recursive: true, force: true });
80
+ }
81
+ catch {
82
+ // Ignore cleanup errors
83
+ }
84
+ }
85
+ /**
86
+ * Create a temporary directory for extracting ZIP files
87
+ */
88
+ export async function createTempDir() {
89
+ return mkdtemp(join(tmpdir(), 'uipro-'));
90
+ }
91
+ /**
92
+ * Find the extracted folder inside temp directory
93
+ * GitHub release ZIPs often contain a single root folder
94
+ */
95
+ async function findExtractedRoot(tempDir) {
96
+ const entries = await readdir(tempDir, { withFileTypes: true });
97
+ const dirs = entries.filter(e => e.isDirectory());
98
+ // If there's exactly one directory, it's likely the extracted root
99
+ if (dirs.length === 1) {
100
+ return join(tempDir, dirs[0].name);
101
+ }
102
+ // Otherwise, assume tempDir itself is the root
103
+ return tempDir;
104
+ }
105
+ /**
106
+ * Install from a downloaded and extracted ZIP file
107
+ */
108
+ export async function installFromZip(zipPath, targetDir, aiType) {
109
+ // Create temp directory
110
+ const tempDir = await createTempDir();
111
+ try {
112
+ // Extract ZIP to temp directory
113
+ await extractZip(zipPath, tempDir);
114
+ // Find the actual root of the extracted content
115
+ const extractedRoot = await findExtractedRoot(tempDir);
116
+ // Copy folders from extracted content to target
117
+ const copiedFolders = await copyFolders(extractedRoot, targetDir, aiType);
118
+ return { copiedFolders, tempDir };
119
+ }
120
+ catch (error) {
121
+ // Cleanup on error
122
+ await cleanup(tempDir);
123
+ throw error;
124
+ }
125
+ }
@@ -0,0 +1,12 @@
1
+ import type { Release } from '../types/index.js';
2
+ export declare class GitHubRateLimitError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare class GitHubDownloadError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ export declare function getGitHubTokenGuidance(): string;
9
+ export declare function fetchReleases(token?: string): Promise<Release[]>;
10
+ export declare function getLatestRelease(token?: string): Promise<Release>;
11
+ export declare function downloadRelease(url: string, dest: string, token?: string): Promise<void>;
12
+ export declare function getAssetUrl(release: Release): string | null;
@@ -0,0 +1,97 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ const REPO_OWNER = 'kterfan';
3
+ const REPO_NAME = 'ui-pro-forge';
4
+ const API_BASE = 'https://api.github.com';
5
+ const USER_AGENT = 'ui-pro-forge-cli';
6
+ export class GitHubRateLimitError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'GitHubRateLimitError';
10
+ }
11
+ }
12
+ export class GitHubDownloadError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'GitHubDownloadError';
16
+ }
17
+ }
18
+ export function getGitHubTokenGuidance() {
19
+ return ('To increase your GitHub API rate limit, set the UI_PRO_MAX_GITHUB_TOKEN environment variable\n' +
20
+ 'to a GitHub Personal Access Token (no scopes needed for public repos).\n' +
21
+ 'Create one at: https://github.com/settings/tokens\n' +
22
+ 'Example: UI_PRO_MAX_GITHUB_TOKEN=ghp_xxx uipro init\n' +
23
+ 'Or pass it directly: uipro init --token ghp_xxx');
24
+ }
25
+ function checkRateLimit(response) {
26
+ const remaining = response.headers.get('x-ratelimit-remaining');
27
+ if (response.status === 403 && remaining === '0') {
28
+ const resetTime = response.headers.get('x-ratelimit-reset');
29
+ const resetDate = resetTime ? new Date(parseInt(resetTime) * 1000).toLocaleTimeString() : 'unknown';
30
+ throw new GitHubRateLimitError(`GitHub API rate limit exceeded. Resets at ${resetDate}.\n${getGitHubTokenGuidance()}`);
31
+ }
32
+ if (response.status === 429) {
33
+ throw new GitHubRateLimitError(`GitHub API rate limit exceeded (429 Too Many Requests).\n${getGitHubTokenGuidance()}`);
34
+ }
35
+ }
36
+ function getAuthHeaders(token) {
37
+ const resolved = (token || process.env['UI_PRO_MAX_GITHUB_TOKEN'] || process.env['GITHUB_TOKEN'])?.trim();
38
+ return resolved ? { 'Authorization': `Bearer ${resolved}` } : {};
39
+ }
40
+ export async function fetchReleases(token) {
41
+ const url = `${API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/releases`;
42
+ const response = await fetch(url, {
43
+ headers: {
44
+ 'Accept': 'application/vnd.github.v3+json',
45
+ 'User-Agent': USER_AGENT,
46
+ ...getAuthHeaders(token),
47
+ },
48
+ });
49
+ checkRateLimit(response);
50
+ if (!response.ok) {
51
+ throw new GitHubDownloadError(`Failed to fetch releases: ${response.status} ${response.statusText}`);
52
+ }
53
+ return response.json();
54
+ }
55
+ export async function getLatestRelease(token) {
56
+ const url = `${API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;
57
+ const response = await fetch(url, {
58
+ headers: {
59
+ 'Accept': 'application/vnd.github.v3+json',
60
+ 'User-Agent': USER_AGENT,
61
+ ...getAuthHeaders(token),
62
+ },
63
+ });
64
+ checkRateLimit(response);
65
+ if (!response.ok) {
66
+ throw new GitHubDownloadError(`Failed to fetch latest release: ${response.status} ${response.statusText}`);
67
+ }
68
+ return response.json();
69
+ }
70
+ export async function downloadRelease(url, dest, token) {
71
+ const response = await fetch(url, {
72
+ headers: {
73
+ 'User-Agent': USER_AGENT,
74
+ 'Accept': 'application/octet-stream',
75
+ ...getAuthHeaders(token),
76
+ },
77
+ });
78
+ checkRateLimit(response);
79
+ if (!response.ok) {
80
+ throw new GitHubDownloadError(`Failed to download: ${response.status} ${response.statusText}`);
81
+ }
82
+ const buffer = await response.arrayBuffer();
83
+ await writeFile(dest, Buffer.from(buffer));
84
+ }
85
+ export function getAssetUrl(release) {
86
+ // First try to find an uploaded ZIP asset
87
+ const asset = release.assets.find(a => a.name.endsWith('.zip'));
88
+ if (asset?.browser_download_url) {
89
+ return asset.browser_download_url;
90
+ }
91
+ // Fall back to GitHub's auto-generated archive
92
+ // Format: https://github.com/{owner}/{repo}/archive/refs/tags/{tag}.zip
93
+ if (release.tag_name) {
94
+ return `https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${release.tag_name}.zip`;
95
+ }
96
+ return null;
97
+ }
@@ -0,0 +1,8 @@
1
+ export declare const logger: {
2
+ info: (msg: string) => void;
3
+ success: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ title: (msg: string) => void;
7
+ dim: (msg: string) => void;
8
+ };
@@ -0,0 +1,9 @@
1
+ import chalk from 'chalk';
2
+ export const logger = {
3
+ info: (msg) => console.log(chalk.blue('info'), msg),
4
+ success: (msg) => console.log(chalk.green('success'), msg),
5
+ warn: (msg) => console.log(chalk.yellow('warn'), msg),
6
+ error: (msg) => console.log(chalk.red('error'), msg),
7
+ title: (msg) => console.log(chalk.bold.cyan(`\n${msg}\n`)),
8
+ dim: (msg) => console.log(chalk.dim(msg)),
9
+ };
@@ -0,0 +1,50 @@
1
+ export interface PlatformConfig {
2
+ platform: string;
3
+ displayName: string;
4
+ installType: 'full' | 'reference';
5
+ folderStructure: {
6
+ root: string;
7
+ skillPath: string;
8
+ filename: string;
9
+ };
10
+ scriptPath: string;
11
+ frontmatter: Record<string, string> | null;
12
+ sections: {
13
+ quickReference: boolean;
14
+ };
15
+ title: string;
16
+ description: string;
17
+ skillOrWorkflow: string;
18
+ }
19
+ /**
20
+ * Load platform configuration from JSON file
21
+ */
22
+ export declare function loadPlatformConfig(aiType: string): Promise<PlatformConfig>;
23
+ /**
24
+ * Load all available platform configs
25
+ */
26
+ export declare function loadAllPlatformConfigs(): Promise<Map<string, PlatformConfig>>;
27
+ /**
28
+ * Render skill file content from template
29
+ * When isGlobal=true, rewrites script paths to use ~/{root}/ prefix
30
+ */
31
+ export declare function renderSkillFile(config: PlatformConfig, isGlobal?: boolean): Promise<string>;
32
+ /**
33
+ * List the static sub-skills bundled under assets/skills/ (everything except
34
+ * the template-rendered orchestrator). Empty if the package predates bundling.
35
+ */
36
+ export declare function listBundledSubSkills(): Promise<string[]>;
37
+ /**
38
+ * Generate platform files for a specific AI type
39
+ * All platforms use self-contained installation with data and scripts
40
+ * When isGlobal=true, installs to ~/home directory with absolute script paths
41
+ */
42
+ export declare function generatePlatformFiles(targetDir: string, aiType: string, isGlobal?: boolean, force?: boolean): Promise<string[]>;
43
+ /**
44
+ * Generate files for all AI types
45
+ */
46
+ export declare function generateAllPlatformFiles(targetDir: string, isGlobal?: boolean, force?: boolean): Promise<string[]>;
47
+ /**
48
+ * Get list of supported AI types
49
+ */
50
+ export declare function getSupportedAITypes(): string[];