claude-code-pilot 3.1.1 → 3.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.
Files changed (198) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +16 -11
  3. package/bin/install.js +127 -11
  4. package/manifest.json +20 -1
  5. package/package.json +4 -3
  6. package/src/agents/a11y-architect.md +141 -0
  7. package/src/agents/code-architect.md +71 -0
  8. package/src/agents/code-explorer.md +69 -0
  9. package/src/agents/code-simplifier.md +47 -0
  10. package/src/agents/comment-analyzer.md +45 -0
  11. package/src/agents/csharp-reviewer.md +101 -0
  12. package/src/agents/dart-build-resolver.md +201 -0
  13. package/src/agents/django-build-resolver.md +252 -0
  14. package/src/agents/django-reviewer.md +169 -0
  15. package/src/agents/fastapi-reviewer.md +79 -0
  16. package/src/agents/fsharp-reviewer.md +109 -0
  17. package/src/agents/pr-test-analyzer.md +45 -0
  18. package/src/agents/silent-failure-hunter.md +50 -0
  19. package/src/agents/swift-build-resolver.md +170 -0
  20. package/src/agents/swift-reviewer.md +116 -0
  21. package/src/agents/type-design-analyzer.md +41 -0
  22. package/src/available-rules/README.md +3 -1
  23. package/src/available-rules/dart/coding-style.md +159 -0
  24. package/src/available-rules/dart/hooks.md +66 -0
  25. package/src/available-rules/dart/patterns.md +261 -0
  26. package/src/available-rules/dart/security.md +135 -0
  27. package/src/available-rules/dart/testing.md +215 -0
  28. package/src/available-rules/web/coding-style.md +105 -0
  29. package/src/available-rules/web/design-quality.md +72 -0
  30. package/src/available-rules/web/hooks.md +129 -0
  31. package/src/available-rules/web/patterns.md +88 -0
  32. package/src/available-rules/web/performance.md +73 -0
  33. package/src/available-rules/web/security.md +66 -0
  34. package/src/available-rules/web/testing.md +64 -0
  35. package/src/commands/ccp/ai-integration-phase.md +36 -0
  36. package/src/commands/ccp/audit-fix.md +33 -0
  37. package/src/commands/ccp/code-review-fix.md +52 -0
  38. package/src/commands/ccp/cost-report.md +107 -0
  39. package/src/commands/ccp/eval-review.md +32 -0
  40. package/src/commands/ccp/extract_learnings.md +22 -0
  41. package/src/commands/ccp/import.md +37 -0
  42. package/src/commands/ccp/ingest-docs.md +42 -0
  43. package/src/commands/ccp/intel.md +179 -0
  44. package/src/commands/ccp/mvp-phase.md +45 -0
  45. package/src/commands/ccp/plan-prd.md +160 -0
  46. package/src/commands/ccp/plan-review-convergence.md +58 -0
  47. package/src/commands/ccp/pr-ecc.md +184 -0
  48. package/src/commands/ccp/scan.md +26 -0
  49. package/src/commands/ccp/security-scan.md +74 -0
  50. package/src/commands/ccp/sketch-wrap-up.md +31 -0
  51. package/src/commands/ccp/sketch.md +54 -0
  52. package/src/commands/ccp/spec-phase.md +62 -0
  53. package/src/commands/ccp/spike-wrap-up.md +31 -0
  54. package/src/commands/ccp/spike.md +51 -0
  55. package/src/commands/ccp/ultraplan-phase.md +33 -0
  56. package/src/hooks/ccp-bash-hook-dispatcher.js +96 -0
  57. package/src/hooks/ccp-context-monitor.js +23 -0
  58. package/src/hooks/ccp-doc-file-warning.js +93 -0
  59. package/src/hooks/ccp-pre-bash-dispatcher.js +24 -0
  60. package/src/hooks/ccp-read-injection-scanner.js +152 -0
  61. package/src/hooks/ccp-write-gateguard.js +868 -0
  62. package/src/hooks/kit-check-update.js +59 -7
  63. package/src/hooks/run-with-flags-shell.sh +1 -0
  64. package/src/hooks/run-with-flags.js +48 -1
  65. package/src/hooks/session-end.js +88 -1
  66. package/src/lib/hook-flags.js +14 -0
  67. package/src/lib/project-detect.js +0 -2
  68. package/src/lib/shell-substitution.js +499 -0
  69. package/src/pilot/references/agent-contracts.md +79 -0
  70. package/src/pilot/references/ai-evals.md +156 -0
  71. package/src/pilot/references/ai-frameworks.md +186 -0
  72. package/src/pilot/references/doc-conflict-engine.md +91 -0
  73. package/src/pilot/references/execute-mvp-tdd.md +81 -0
  74. package/src/pilot/references/gate-prompts.md +100 -0
  75. package/src/pilot/references/gates.md +70 -0
  76. package/src/pilot/references/mandatory-initial-read.md +2 -0
  77. package/src/pilot/references/mvp-concepts.md +49 -0
  78. package/src/pilot/references/planner-graphify-auto-update.md +67 -0
  79. package/src/pilot/references/planner-human-verify-mode.md +57 -0
  80. package/src/pilot/references/planner-mvp-mode.md +53 -0
  81. package/src/pilot/references/project-skills-discovery.md +19 -0
  82. package/src/pilot/references/revision-loop.md +97 -0
  83. package/src/pilot/references/skeleton-template.md +48 -0
  84. package/src/pilot/references/sketch-interactivity.md +41 -0
  85. package/src/pilot/references/sketch-theme-system.md +94 -0
  86. package/src/pilot/references/sketch-tooling.md +45 -0
  87. package/src/pilot/references/sketch-variant-patterns.md +81 -0
  88. package/src/pilot/references/spidr-splitting.md +69 -0
  89. package/src/pilot/references/thinking-models-debug.md +44 -0
  90. package/src/pilot/references/thinking-models-execution.md +50 -0
  91. package/src/pilot/references/thinking-models-planning.md +62 -0
  92. package/src/pilot/references/thinking-models-research.md +50 -0
  93. package/src/pilot/references/thinking-models-verification.md +55 -0
  94. package/src/pilot/references/user-story-template.md +58 -0
  95. package/src/pilot/references/verify-mvp-mode.md +85 -0
  96. package/src/pilot/references/worktree-path-safety.md +89 -0
  97. package/src/pilot/templates/AI-SPEC.md +246 -0
  98. package/src/pilot/templates/spec.md +307 -0
  99. package/src/pilot/workflows/ai-integration-phase.md +284 -0
  100. package/src/pilot/workflows/audit-fix.md +175 -0
  101. package/src/pilot/workflows/code-review-fix.md +497 -0
  102. package/src/pilot/workflows/eval-review.md +155 -0
  103. package/src/pilot/workflows/extract_learnings.md +242 -0
  104. package/src/pilot/workflows/help.md +5 -0
  105. package/src/pilot/workflows/import.md +246 -0
  106. package/src/pilot/workflows/ingest-docs.md +328 -0
  107. package/src/pilot/workflows/mvp-phase.md +199 -0
  108. package/src/pilot/workflows/plan-review-convergence.md +329 -0
  109. package/src/pilot/workflows/scan.md +102 -0
  110. package/src/pilot/workflows/sketch-wrap-up.md +285 -0
  111. package/src/pilot/workflows/sketch.md +360 -0
  112. package/src/pilot/workflows/spec-phase.md +262 -0
  113. package/src/pilot/workflows/spike-wrap-up.md +306 -0
  114. package/src/pilot/workflows/spike.md +452 -0
  115. package/src/pilot/workflows/ultraplan-phase.md +189 -0
  116. package/src/skills/accessibility/SKILL.md +146 -0
  117. package/src/skills/agent-architecture-audit/SKILL.md +256 -0
  118. package/src/skills/agent-eval/SKILL.md +145 -0
  119. package/src/skills/agent-harness-design/SKILL.md +73 -0
  120. package/src/skills/agent-introspection-debugging/SKILL.md +153 -0
  121. package/src/skills/android-clean-architecture/SKILL.md +339 -0
  122. package/src/skills/angular-developer/SKILL.md +154 -0
  123. package/src/skills/angular-developer/references/angular-animations.md +160 -0
  124. package/src/skills/angular-developer/references/angular-aria.md +410 -0
  125. package/src/skills/angular-developer/references/cli.md +86 -0
  126. package/src/skills/angular-developer/references/component-harnesses.md +59 -0
  127. package/src/skills/angular-developer/references/component-styling.md +91 -0
  128. package/src/skills/angular-developer/references/components.md +117 -0
  129. package/src/skills/angular-developer/references/creating-services.md +97 -0
  130. package/src/skills/angular-developer/references/data-resolvers.md +69 -0
  131. package/src/skills/angular-developer/references/define-routes.md +67 -0
  132. package/src/skills/angular-developer/references/defining-providers.md +72 -0
  133. package/src/skills/angular-developer/references/di-fundamentals.md +120 -0
  134. package/src/skills/angular-developer/references/e2e-testing.md +56 -0
  135. package/src/skills/angular-developer/references/effects.md +83 -0
  136. package/src/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  137. package/src/skills/angular-developer/references/host-elements.md +80 -0
  138. package/src/skills/angular-developer/references/injection-context.md +63 -0
  139. package/src/skills/angular-developer/references/inputs.md +101 -0
  140. package/src/skills/angular-developer/references/linked-signal.md +59 -0
  141. package/src/skills/angular-developer/references/loading-strategies.md +61 -0
  142. package/src/skills/angular-developer/references/mcp.md +108 -0
  143. package/src/skills/angular-developer/references/navigate-to-routes.md +69 -0
  144. package/src/skills/angular-developer/references/outputs.md +86 -0
  145. package/src/skills/angular-developer/references/reactive-forms.md +122 -0
  146. package/src/skills/angular-developer/references/rendering-strategies.md +44 -0
  147. package/src/skills/angular-developer/references/resource.md +77 -0
  148. package/src/skills/angular-developer/references/route-animations.md +56 -0
  149. package/src/skills/angular-developer/references/route-guards.md +52 -0
  150. package/src/skills/angular-developer/references/router-lifecycle.md +45 -0
  151. package/src/skills/angular-developer/references/router-testing.md +87 -0
  152. package/src/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  153. package/src/skills/angular-developer/references/signal-forms.md +795 -0
  154. package/src/skills/angular-developer/references/signals-overview.md +94 -0
  155. package/src/skills/angular-developer/references/tailwind-css.md +69 -0
  156. package/src/skills/angular-developer/references/template-driven-forms.md +114 -0
  157. package/src/skills/angular-developer/references/testing-fundamentals.md +65 -0
  158. package/src/skills/api-connector-builder/SKILL.md +120 -0
  159. package/src/skills/code-tour/SKILL.md +236 -0
  160. package/src/skills/compose-multiplatform-patterns/SKILL.md +299 -0
  161. package/src/skills/csharp-testing/SKILL.md +321 -0
  162. package/src/skills/dart-flutter-patterns/SKILL.md +563 -0
  163. package/src/skills/dashboard-builder/SKILL.md +108 -0
  164. package/src/skills/dotnet-patterns/SKILL.md +321 -0
  165. package/src/skills/error-handling/SKILL.md +376 -0
  166. package/src/skills/fastapi-patterns/SKILL.md +327 -0
  167. package/src/skills/flox-environments/SKILL.md +496 -0
  168. package/src/skills/frontend-design/SKILL.md +145 -0
  169. package/src/skills/frontend-slides/SKILL.md +184 -0
  170. package/src/skills/frontend-slides/STYLE_PRESETS.md +330 -0
  171. package/src/skills/fsharp-testing/SKILL.md +280 -0
  172. package/src/skills/gateguard/SKILL.md +121 -0
  173. package/src/skills/github-ops/SKILL.md +144 -0
  174. package/src/skills/hookify-rules/SKILL.md +128 -0
  175. package/src/skills/ios-icon-gen/SKILL.md +157 -0
  176. package/src/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
  177. package/src/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
  178. package/src/skills/knowledge-ops/SKILL.md +154 -0
  179. package/src/skills/liquid-glass-design/SKILL.md +279 -0
  180. package/src/skills/make-interfaces-feel-better/SKILL.md +151 -0
  181. package/src/skills/mysql-patterns/SKILL.md +412 -0
  182. package/src/skills/nestjs-patterns/SKILL.md +230 -0
  183. package/src/skills/plan-orchestrate/SKILL.md +220 -0
  184. package/src/skills/prisma-patterns/SKILL.md +371 -0
  185. package/src/skills/production-audit/SKILL.md +206 -0
  186. package/src/skills/security-bounty-hunter/SKILL.md +99 -0
  187. package/src/skills/security-scan/references/agentshield-policy-exception/candidate-playbook.md +49 -0
  188. package/src/skills/security-scan/references/agentshield-policy-exception/report.json +35 -0
  189. package/src/skills/security-scan/references/agentshield-policy-exception/scenario.json +62 -0
  190. package/src/skills/security-scan/references/agentshield-policy-exception/trace.json +45 -0
  191. package/src/skills/security-scan/references/agentshield-policy-exception/verifier-result.json +35 -0
  192. package/src/skills/swift-actor-persistence/SKILL.md +143 -0
  193. package/src/skills/swift-protocol-di-testing/SKILL.md +190 -0
  194. package/src/skills/swiftui-patterns/SKILL.md +259 -0
  195. package/src/skills/terminal-ops/SKILL.md +109 -0
  196. package/src/skills/ui-demo/SKILL.md +465 -0
  197. package/src/skills/vite-patterns/SKILL.md +449 -0
  198. package/src/skills/windows-desktop-e2e/SKILL.md +887 -0
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: ios-icon-gen
3
+ description: Generate iOS app icons as PNG imagesets for Xcode asset catalogs from SF Symbols (5000+ Apple-native) or Iconify API (275k+ open source icons from 200+ collections). Use when generating icons, creating icon assets, adding icons to asset catalog, or searching for icons for iOS projects.
4
+ origin: community
5
+ ---
6
+
7
+ # iOS Icon Generator
8
+
9
+ Generate PNG icon imagesets for Xcode asset catalogs from two sources.
10
+
11
+ ## When to Activate
12
+
13
+ - Generating icon assets for an iOS/macOS Xcode project
14
+ - Searching for icons across open source collections
15
+ - Creating PNG imagesets (1x, 2x, 3x) for asset catalogs
16
+ - Replacing placeholder icons with production-quality assets
17
+ - Matching existing icon styles in an Xcode project
18
+
19
+ ## Core Principles
20
+
21
+ ### 1. Two Sources, One Output Format
22
+ Both sources produce identical Xcode-compatible imagesets. Choose based on need:
23
+
24
+ | Source | Icons | Requires | Best for |
25
+ |--------|-------|----------|----------|
26
+ | **Iconify API** | 275,000+ from 200+ collections | Internet | Wide selection, specific styles, open source icons |
27
+ | **SF Symbols** | 5,000+ Apple symbols | macOS only | Apple-native style, offline use |
28
+
29
+ ### 2. Always Match Existing Style
30
+ Before generating, check the project's existing icons for size, color, and weight consistency.
31
+
32
+ ### 3. Output Structure
33
+ Both methods produce a complete Xcode imageset:
34
+
35
+ ```
36
+ <output-dir>/<asset-name>.imageset/
37
+ Contents.json
38
+ <asset-name>.png # 1x (68px default)
39
+ <asset-name>@2x.png # 2x (136px default)
40
+ <asset-name>@3x.png # 3x (204px default)
41
+ ```
42
+
43
+ ## Examples
44
+
45
+ ### Step 1: Assess Requirements
46
+
47
+ Determine icon needs: what the icon represents, preferred style, target color, and size.
48
+
49
+ If the project already has icons, check existing style:
50
+ ```bash
51
+ # Check dimensions of existing icon
52
+ sips -g pixelWidth -g pixelHeight path/to/existing@2x.png
53
+ ```
54
+
55
+ ### Step 2: Search for Icons
56
+
57
+ **Iconify API (recommended for wide selection):**
58
+ ```bash
59
+ # Search all collections
60
+ $SKILL_DIR/scripts/iconify_gen.sh search "receipt"
61
+
62
+ # Search within a specific collection
63
+ $SKILL_DIR/scripts/iconify_gen.sh search "business card" --prefix mdi
64
+
65
+ # List available collections
66
+ $SKILL_DIR/scripts/iconify_gen.sh collections
67
+ ```
68
+
69
+ **SF Symbols (for Apple-native style):**
70
+ Browse the SF Symbols app or reference common names:
71
+
72
+ | Use Case | Symbol Name |
73
+ |----------|-------------|
74
+ | Document | `doc.text`, `doc.fill` |
75
+ | Receipt | `doc.text.below.ecg`, `receipt` |
76
+ | Person | `person.crop.rectangle`, `person.text.rectangle` |
77
+ | Camera | `camera`, `camera.fill` |
78
+ | Scan | `doc.viewfinder`, `qrcode.viewfinder` |
79
+ | Settings | `gearshape`, `slider.horizontal.3` |
80
+
81
+ ### Step 3: Preview (Optional)
82
+
83
+ ```bash
84
+ # Iconify preview
85
+ $SKILL_DIR/scripts/iconify_gen.sh preview mdi:receipt-text-outline
86
+ ```
87
+
88
+ ### Step 4: Generate
89
+
90
+ **Iconify API:**
91
+ ```bash
92
+ # Basic generation
93
+ $SKILL_DIR/scripts/iconify_gen.sh mdi:receipt-text-outline editTool_expenseReport
94
+
95
+ # Custom color and output location
96
+ $SKILL_DIR/scripts/iconify_gen.sh mdi:receipt-text-outline myIcon --color 007AFF --output ./Assets.xcassets/icons
97
+ ```
98
+
99
+ Options: `--size <pt>` (default: 68), `--color <hex>` (default: 8E8E93), `--output <dir>` (default: /tmp/icons)
100
+
101
+ **SF Symbols:**
102
+ ```bash
103
+ # Basic generation
104
+ swift $SKILL_DIR/scripts/generate_icons.swift doc.text.below.ecg editTool_expenseReport
105
+
106
+ # Custom color, weight, and output
107
+ swift $SKILL_DIR/scripts/generate_icons.swift person.crop.rectangle myIcon --color 007AFF --weight regular --output ./Assets.xcassets/icons
108
+ ```
109
+
110
+ Options: `--size <pt>` (default: 68), `--color <hex>` (default: 8E8E93), `--weight <name>` (default: thin), `--output <dir>` (default: /tmp/icons)
111
+
112
+ ### Step 5: Verify and Integrate
113
+
114
+ 1. Read the generated @2x PNG to verify visually
115
+ 2. Copy to asset catalog if not output there directly:
116
+ ```bash
117
+ cp -r /tmp/icons/<name>.imageset path/to/Assets.xcassets/<group>/
118
+ ```
119
+ 3. Build the project to verify Xcode picks up the new assets
120
+
121
+ ## Popular Iconify Collections
122
+
123
+ | Prefix | Name | Count | Style |
124
+ |--------|------|-------|-------|
125
+ | `mdi` | Material Design Icons | 7400+ | Filled + outline variants |
126
+ | `ph` | Phosphor | 9000+ | 6 weights per icon |
127
+ | `solar` | Solar | 7400+ | Bold, linear, outline |
128
+ | `tabler` | Tabler Icons | 6000+ | Consistent stroke width |
129
+ | `lucide` | Lucide | 1700+ | Clean, minimal |
130
+ | `ri` | Remix Icon | 3100+ | Filled + line variants |
131
+ | `carbon` | Carbon | 2400+ | IBM design language |
132
+ | `heroicons` | HeroIcons | 1200+ | Tailwind CSS companion |
133
+
134
+ Browse all: <https://icon-sets.iconify.design/>
135
+
136
+ ## Scripts Reference
137
+
138
+ | Script | Source | Path |
139
+ |--------|--------|------|
140
+ | `iconify_gen.sh` | Iconify API (275k+ icons) | `$SKILL_DIR/scripts/iconify_gen.sh` |
141
+ | `generate_icons.swift` | SF Symbols (5k+ icons) | `$SKILL_DIR/scripts/generate_icons.swift` |
142
+
143
+ ## Best Practices
144
+
145
+ - **Search before generating** -- browse available icons to find the best match
146
+ - **Match existing project style** -- check dimensions, color, and weight of existing icons before generating new ones
147
+ - **Use Iconify for variety** -- 200+ collections means you can find the exact style you need
148
+ - **Use SF Symbols for Apple consistency** -- they match system UI perfectly
149
+ - **Generate directly to asset catalog** -- use `--output ./Assets.xcassets/icons` to skip manual copying
150
+ - **Verify visually** -- always preview the @2x PNG before committing
151
+
152
+ ## Anti-Patterns
153
+
154
+ - Generating icons without checking existing project icon style
155
+ - Using default colors when the project has a defined color palette
156
+ - Generating at wrong sizes (check existing icons first)
157
+ - Committing generated icons without visual verification
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env swift
2
+
3
+ import AppKit
4
+ import Foundation
5
+
6
+ // MARK: - Configuration
7
+
8
+ struct IconSpec {
9
+ let symbolName: String
10
+ let assetName: String
11
+ let baseSize: CGFloat
12
+ let color: NSColor
13
+ let weight: NSFont.Weight
14
+ }
15
+
16
+ func parseColor(_ hex: String) -> NSColor {
17
+ var hex = hex.trimmingCharacters(in: .whitespacesAndNewlines)
18
+ if hex.hasPrefix("#") { hex.removeFirst() }
19
+ guard hex.count == 6, let value = UInt64(hex, radix: 16) else {
20
+ return NSColor(red: 142/255, green: 142/255, blue: 147/255, alpha: 1.0)
21
+ }
22
+ return NSColor(
23
+ red: CGFloat((value >> 16) & 0xFF) / 255,
24
+ green: CGFloat((value >> 8) & 0xFF) / 255,
25
+ blue: CGFloat(value & 0xFF) / 255,
26
+ alpha: 1.0
27
+ )
28
+ }
29
+
30
+ func parseWeight(_ name: String) -> NSFont.Weight {
31
+ switch name.lowercased() {
32
+ case "ultralight": return .ultraLight
33
+ case "thin": return .thin
34
+ case "light": return .light
35
+ case "regular": return .regular
36
+ case "medium": return .medium
37
+ case "semibold": return .semibold
38
+ case "bold": return .bold
39
+ case "heavy": return .heavy
40
+ case "black": return .black
41
+ default: return .thin
42
+ }
43
+ }
44
+
45
+ // MARK: - Generation
46
+
47
+ enum IconError: Error, CustomStringConvertible {
48
+ case directoryCreation(String)
49
+ case symbolNotFound(String)
50
+ case configurationFailed(String)
51
+ case pngCreation(String)
52
+ case fileWrite(String)
53
+
54
+ var description: String {
55
+ switch self {
56
+ case .directoryCreation(let msg): return msg
57
+ case .symbolNotFound(let msg): return msg
58
+ case .configurationFailed(let msg): return msg
59
+ case .pngCreation(let msg): return msg
60
+ case .fileWrite(let msg): return msg
61
+ }
62
+ }
63
+ }
64
+
65
+ func generateIcon(_ spec: IconSpec, outputDir: String) throws {
66
+ let dir = "\(outputDir)/\(spec.assetName).imageset"
67
+ do {
68
+ try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true)
69
+ } catch {
70
+ throw IconError.directoryCreation("Could not create output directory '\(dir)': \(error.localizedDescription)")
71
+ }
72
+
73
+ let scales: [(suffix: String, multiplier: CGFloat)] = [("", 1), ("@2x", 2), ("@3x", 3)]
74
+
75
+ for scale in scales {
76
+ let pixelSize = spec.baseSize * scale.multiplier
77
+ let imageSize = NSSize(width: pixelSize, height: pixelSize)
78
+
79
+ let config = NSImage.SymbolConfiguration(
80
+ pointSize: pixelSize * 0.40,
81
+ weight: spec.weight,
82
+ scale: .large
83
+ )
84
+
85
+ guard let symbol = NSImage(systemSymbolName: spec.symbolName, accessibilityDescription: nil) else {
86
+ throw IconError.symbolNotFound("SF Symbol '\(spec.symbolName)' not found. Run 'SF Symbols' app to browse available names.")
87
+ }
88
+
89
+ guard let configured = symbol.withSymbolConfiguration(config) else {
90
+ throw IconError.configurationFailed("Could not apply symbol configuration to '\(spec.symbolName)'")
91
+ }
92
+
93
+ let image = NSImage(size: imageSize, flipped: false) { rect in
94
+ let symSize = configured.size
95
+ let x = (rect.width - symSize.width) / 2
96
+ let y = (rect.height - symSize.height) / 2
97
+ let drawRect = NSRect(x: x, y: y, width: symSize.width, height: symSize.height)
98
+
99
+ let tinted = NSImage(size: symSize, flipped: false) { tintRect in
100
+ configured.draw(in: tintRect)
101
+ spec.color.set()
102
+ tintRect.fill(using: .sourceAtop)
103
+ return true
104
+ }
105
+
106
+ tinted.draw(in: drawRect, from: .zero, operation: .sourceOver, fraction: 1.0)
107
+ return true
108
+ }
109
+
110
+ guard let tiffData = image.tiffRepresentation,
111
+ let bitmap = NSBitmapImageRep(data: tiffData),
112
+ let pngData = bitmap.representation(using: .png, properties: [:]) else {
113
+ throw IconError.pngCreation("Failed to create PNG for \(spec.assetName)\(scale.suffix)")
114
+ }
115
+
116
+ let fileName = "\(spec.assetName)\(scale.suffix).png"
117
+ do {
118
+ try pngData.write(to: URL(fileURLWithPath: "\(dir)/\(fileName)"))
119
+ } catch {
120
+ throw IconError.fileWrite("Failed to write \(fileName): \(error.localizedDescription)")
121
+ }
122
+ print(" \(fileName) (\(Int(pixelSize))x\(Int(pixelSize)))")
123
+ }
124
+
125
+ // Write Contents.json
126
+ let json = """
127
+ {
128
+ "images" : [
129
+ {
130
+ "filename" : "\(spec.assetName).png",
131
+ "idiom" : "universal",
132
+ "scale" : "1x"
133
+ },
134
+ {
135
+ "filename" : "\(spec.assetName)@2x.png",
136
+ "idiom" : "universal",
137
+ "scale" : "2x"
138
+ },
139
+ {
140
+ "filename" : "\(spec.assetName)@3x.png",
141
+ "idiom" : "universal",
142
+ "scale" : "3x"
143
+ }
144
+ ],
145
+ "info" : {
146
+ "author" : "xcode",
147
+ "version" : 1
148
+ }
149
+ }
150
+ """
151
+ do {
152
+ try json.write(toFile: "\(dir)/Contents.json", atomically: true, encoding: .utf8)
153
+ } catch {
154
+ throw IconError.fileWrite("Failed to write Contents.json: \(error.localizedDescription)")
155
+ }
156
+ }
157
+
158
+ func requireOptionValue(_ args: [String], at index: Int, flag: String) -> String {
159
+ guard index < args.count else {
160
+ fputs("ERROR: Missing value for \(flag)\n", stderr)
161
+ exit(1)
162
+ }
163
+ let value = args[index]
164
+ if value.hasPrefix("--") {
165
+ fputs("ERROR: Missing value for \(flag)\n", stderr)
166
+ exit(1)
167
+ }
168
+ return value
169
+ }
170
+
171
+ // MARK: - CLI
172
+
173
+ let args = CommandLine.arguments
174
+
175
+ if args.count < 3 || args.contains("--help") || args.contains("-h") {
176
+ print("""
177
+ Usage: generate_icons.swift <sf-symbol-name> <asset-name> [options]
178
+
179
+ Options:
180
+ --size <pt> Base size in points (default: 68)
181
+ --color <hex> Color hex code (default: 8E8E93)
182
+ --weight <name> Font weight: ultralight|thin|light|regular|medium|semibold|bold|heavy|black (default: thin)
183
+ --output <dir> Output directory (default: /tmp/icons)
184
+
185
+ Examples:
186
+ generate_icons.swift doc.text.below.ecg editTool_expenseReport
187
+ generate_icons.swift person.crop.rectangle editTool_businessCard --color 007AFF --weight regular
188
+ generate_icons.swift receipt myReceipt --size 48 --output ./Assets.xcassets/icons
189
+
190
+ Browse SF Symbol names: open the SF Symbols app (free from Apple) or https://developer.apple.com/sf-symbols/
191
+ """)
192
+ exit(0)
193
+ }
194
+
195
+ let symbolName = args[1]
196
+ let assetName = args[2]
197
+
198
+ var baseSize: CGFloat = 68
199
+ var colorHex = "8E8E93"
200
+ var weightName = "thin"
201
+ var outputDir = "/tmp/icons"
202
+
203
+ var i = 3
204
+ while i < args.count {
205
+ switch args[i] {
206
+ case "--size":
207
+ let raw = requireOptionValue(args, at: i + 1, flag: "--size")
208
+ guard let size = Double(raw), size > 0 else {
209
+ fputs("ERROR: --size must be a positive number\n", stderr)
210
+ exit(1)
211
+ }
212
+ baseSize = CGFloat(size)
213
+ i += 2
214
+ continue
215
+ case "--color":
216
+ colorHex = requireOptionValue(args, at: i + 1, flag: "--color")
217
+ let stripped = colorHex.hasPrefix("#") ? String(colorHex.dropFirst()) : colorHex
218
+ guard stripped.count == 6, UInt64(stripped, radix: 16) != nil else {
219
+ fputs("ERROR: --color must be a 6-digit hex code (e.g. 007AFF)\n", stderr)
220
+ exit(1)
221
+ }
222
+ i += 2
223
+ continue
224
+ case "--weight":
225
+ weightName = requireOptionValue(args, at: i + 1, flag: "--weight")
226
+ let validWeights = ["ultralight", "thin", "light", "regular", "medium", "semibold", "bold", "heavy", "black"]
227
+ guard validWeights.contains(weightName.lowercased()) else {
228
+ fputs("ERROR: --weight must be one of: \(validWeights.joined(separator: ", "))\n", stderr)
229
+ exit(1)
230
+ }
231
+ i += 2
232
+ continue
233
+ case "--output":
234
+ outputDir = requireOptionValue(args, at: i + 1, flag: "--output")
235
+ i += 2
236
+ continue
237
+ default:
238
+ fputs("WARNING: Unknown option \(args[i])\n", stderr)
239
+ }
240
+ i += 1
241
+ }
242
+
243
+ let spec = IconSpec(
244
+ symbolName: symbolName,
245
+ assetName: assetName,
246
+ baseSize: baseSize,
247
+ color: parseColor(colorHex),
248
+ weight: parseWeight(weightName)
249
+ )
250
+
251
+ print("Generating \(assetName) from SF Symbol '\(symbolName)':")
252
+ do {
253
+ try generateIcon(spec, outputDir: outputDir)
254
+ print("Output: \(outputDir)/\(assetName).imageset/")
255
+ } catch {
256
+ fputs("ERROR: \(error)\n", stderr)
257
+ exit(1)
258
+ }
@@ -0,0 +1,235 @@
1
+ #!/bin/bash
2
+ #
3
+ # Generate iOS icon imagesets from Iconify API (275k+ open source icons)
4
+ # Uses: curl (download SVG) + sips (SVG->PNG conversion, built into macOS)
5
+ #
6
+ # Usage:
7
+ # iconify_gen.sh <icon-id> <asset-name> [options]
8
+ # iconify_gen.sh search <query> [--prefix <collection>] [--limit <n>]
9
+ #
10
+ # Examples:
11
+ # iconify_gen.sh mdi:receipt-text-outline myExpenseIcon
12
+ # iconify_gen.sh search "business card"
13
+ # iconify_gen.sh search receipt --prefix mdi
14
+
15
+ set -euo pipefail
16
+
17
+ API_BASE="https://api.iconify.design"
18
+ readonly CURL_OPTS=(--fail --silent --show-error --connect-timeout 10 --max-time 30)
19
+
20
+ # Defaults
21
+ SIZE=68
22
+ COLOR="8E8E93"
23
+ OUTPUT="/tmp/icons"
24
+ LIMIT=20
25
+
26
+ require_value() {
27
+ local flag="$1"
28
+ local value="${2-}"
29
+ if [[ -z "$value" || "$value" == --* ]]; then
30
+ echo "ERROR: ${flag} requires a value" >&2
31
+ exit 1
32
+ fi
33
+ }
34
+
35
+ usage() {
36
+ cat <<'EOF'
37
+ Usage:
38
+ iconify_gen.sh <icon-id> <asset-name> [options] Generate an icon imageset
39
+ iconify_gen.sh search <query> [options] Search for icons
40
+ iconify_gen.sh preview <icon-id> Download preview SVG
41
+ iconify_gen.sh collections List popular icon collections
42
+
43
+ Generate Options:
44
+ --size <pt> Base size in points (default: 68)
45
+ --color <hex> Color hex without # (default: 8E8E93)
46
+ --output <dir> Output directory (default: /tmp/icons)
47
+
48
+ Search Options:
49
+ --prefix <name> Filter by collection (e.g., mdi, lucide, tabler, ph)
50
+ --limit <n> Max results (default: 20)
51
+
52
+ Icon ID Format: <collection>:<icon-name>
53
+ Examples: mdi:receipt-text-outline, lucide:credit-card, ph:address-book
54
+
55
+ Popular Collections:
56
+ mdi Material Design Icons (7400+ icons)
57
+ lucide Lucide (1700+ icons)
58
+ tabler Tabler Icons (6000+ icons)
59
+ ph Phosphor (9000+ icons)
60
+ ri Remix Icon (2800+ icons)
61
+ carbon Carbon (2100+ icons)
62
+ EOF
63
+ exit 0
64
+ }
65
+
66
+ search_icons() {
67
+ local query="$1"
68
+ shift
69
+ local prefix=""
70
+
71
+ while [[ $# -gt 0 ]]; do
72
+ case "$1" in
73
+ --prefix) require_value --prefix "${2-}"; prefix="$2"; shift 2 ;;
74
+ --limit) require_value --limit "${2-}"; LIMIT="$2"; shift 2 ;;
75
+ *) shift ;;
76
+ esac
77
+ done
78
+
79
+ local encoded_query
80
+ encoded_query="$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$query")"
81
+ local url="${API_BASE}/search?query=${encoded_query}&limit=${LIMIT}"
82
+ if [[ -n "$prefix" ]]; then
83
+ url="${url}&prefix=${prefix}"
84
+ fi
85
+
86
+ local response
87
+ response=$(curl "${CURL_OPTS[@]}" "$url") || { echo "ERROR: Search request failed"; exit 1; }
88
+
89
+ local total
90
+ total=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('total',0))")
91
+
92
+ echo "Found ${total} icons for '${query}':"
93
+ echo ""
94
+ echo "$response" | python3 -c "
95
+ import sys, json
96
+ data = json.load(sys.stdin)
97
+ for icon in data.get('icons', []):
98
+ print(f' {icon}')
99
+ "
100
+ echo ""
101
+ echo "Generate with: iconify_gen.sh <icon-id> <asset-name>"
102
+ echo "Preview with: iconify_gen.sh preview <icon-id>"
103
+ }
104
+
105
+ list_collections() {
106
+ echo "Popular Iconify collections:"
107
+ echo ""
108
+ local resp
109
+ resp=$(curl "${CURL_OPTS[@]}" "${API_BASE}/collections") || { echo "ERROR: Failed to fetch collections list"; exit 1; }
110
+ echo "$resp" | python3 -c "
111
+ import sys, json
112
+ data = json.load(sys.stdin)
113
+ popular = ['mdi','lucide','tabler','ph','ri','carbon','solar','heroicons','bi','octicon','ion','fe','charm','ci','iconoir','basil','uil','mingcute','flowbite','mynaui']
114
+ for k in popular:
115
+ if k in data:
116
+ v = data[k]
117
+ name = v.get('name','')
118
+ total = v.get('total',0)
119
+ print(f' {k:12s} {name} ({total} icons)')
120
+ "
121
+ echo ""
122
+ echo "Full list: https://icon-sets.iconify.design/"
123
+ }
124
+
125
+ preview_icon() {
126
+ local icon_id="$1"
127
+ local collection="${icon_id%%:*}"
128
+ local name="${icon_id#*:}"
129
+ local url="${API_BASE}/${collection}/${name}.svg?width=136&height=136&color=%23${COLOR}"
130
+ local outfile="/tmp/iconify_preview_${collection}_${name}.svg"
131
+
132
+ curl "${CURL_OPTS[@]}" "$url" -o "$outfile" || { echo "ERROR: Icon '${icon_id}' not found"; exit 1; }
133
+ echo "Preview SVG: ${outfile}"
134
+ echo "URL: ${url}"
135
+
136
+ # Also convert to PNG for visual check
137
+ local pngfile="/tmp/iconify_preview_${collection}_${name}.png"
138
+ sips -s format png "$outfile" --out "$pngfile" >/dev/null 2>&1 || echo "WARNING: sips conversion failed; PNG may be incorrect"
139
+ echo "Preview PNG: ${pngfile}"
140
+ }
141
+
142
+ generate_icon() {
143
+ local icon_id="$1"
144
+ local asset_name="$2"
145
+ shift 2
146
+
147
+ while [[ $# -gt 0 ]]; do
148
+ case "$1" in
149
+ --size) require_value --size "${2-}"; SIZE="$2"; shift 2 ;;
150
+ --color) require_value --color "${2-}"; COLOR="$2"; shift 2 ;;
151
+ --output) require_value --output "${2-}"; OUTPUT="$2"; shift 2 ;;
152
+ *) shift ;;
153
+ esac
154
+ done
155
+
156
+ local collection="${icon_id%%:*}"
157
+ local name="${icon_id#*:}"
158
+ local imageset_dir="${OUTPUT}/${asset_name}.imageset"
159
+
160
+ mkdir -p "$imageset_dir"
161
+
162
+ echo "Generating ${asset_name} from Iconify '${icon_id}':"
163
+
164
+ local scales=("1:${SIZE}" "2:$((SIZE * 2))" "3:$((SIZE * 3))")
165
+
166
+ for scale_info in "${scales[@]}"; do
167
+ local scale="${scale_info%%:*}"
168
+ local px="${scale_info#*:}"
169
+ local suffix=""
170
+ [[ "$scale" != "1" ]] && suffix="@${scale}x"
171
+
172
+ local svg_url="${API_BASE}/${collection}/${name}.svg?width=${px}&height=${px}&color=%23${COLOR}"
173
+ local svg_file="${imageset_dir}/${asset_name}${suffix}.svg"
174
+ local png_file="${imageset_dir}/${asset_name}${suffix}.png"
175
+
176
+ curl "${CURL_OPTS[@]}" "$svg_url" -o "$svg_file" || { echo "ERROR: Failed to download icon '${icon_id}'"; exit 1; }
177
+ sips -s format png "$svg_file" --out "$png_file" >/dev/null 2>&1 || echo "WARNING: sips conversion may have failed for ${svg_file}"
178
+ rm "$svg_file"
179
+
180
+ echo " ${asset_name}${suffix}.png (${px}x${px})"
181
+ done
182
+
183
+ # Write Contents.json
184
+ cat > "${imageset_dir}/Contents.json" <<JSONEOF
185
+ {
186
+ "images" : [
187
+ {
188
+ "filename" : "${asset_name}.png",
189
+ "idiom" : "universal",
190
+ "scale" : "1x"
191
+ },
192
+ {
193
+ "filename" : "${asset_name}@2x.png",
194
+ "idiom" : "universal",
195
+ "scale" : "2x"
196
+ },
197
+ {
198
+ "filename" : "${asset_name}@3x.png",
199
+ "idiom" : "universal",
200
+ "scale" : "3x"
201
+ }
202
+ ],
203
+ "info" : {
204
+ "author" : "xcode",
205
+ "version" : 1
206
+ }
207
+ }
208
+ JSONEOF
209
+
210
+ echo "Output: ${imageset_dir}/"
211
+ }
212
+
213
+ # Main
214
+ [[ $# -eq 0 ]] && usage
215
+ [[ "$1" == "--help" || "$1" == "-h" ]] && usage
216
+
217
+ case "$1" in
218
+ search)
219
+ shift
220
+ [[ $# -eq 0 ]] && { echo "Usage: iconify_gen.sh search <query>"; exit 1; }
221
+ search_icons "$@"
222
+ ;;
223
+ preview)
224
+ shift
225
+ [[ $# -eq 0 ]] && { echo "Usage: iconify_gen.sh preview <icon-id>"; exit 1; }
226
+ preview_icon "$1"
227
+ ;;
228
+ collections)
229
+ list_collections
230
+ ;;
231
+ *)
232
+ [[ $# -lt 2 ]] && { echo "Usage: iconify_gen.sh <icon-id> <asset-name> [options]"; exit 1; }
233
+ generate_icon "$@"
234
+ ;;
235
+ esac