@hienlh/ppm 0.13.67 → 0.13.68

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 (72) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +3 -1
  5. package/bun.lock +2170 -0
  6. package/bunfig.toml +2 -0
  7. package/dist/web/assets/architecture-PBZL5I3N-CS5Rvu_a.js +1 -0
  8. package/dist/web/assets/{audio-preview-Iq-XRBGw.js → audio-preview-DLz0dEw6.js} +1 -1
  9. package/dist/web/assets/{chat-tab-DkVXRD9e.js → chat-tab-B0L_i2ls.js} +3 -3
  10. package/dist/web/assets/{code-editor-M6wHw8AZ.js → code-editor-BEdecTYr.js} +2 -2
  11. package/dist/web/assets/{conflict-editor-D_8t44Wi.js → conflict-editor-CkUhGJsN.js} +1 -1
  12. package/dist/web/assets/{database-viewer-Cj5yCn4w.js → database-viewer-CRBrhKbW.js} +1 -1
  13. package/dist/web/assets/{diff-viewer-BgPv67fJ.js → diff-viewer-DcyIipTd.js} +1 -1
  14. package/dist/web/assets/{docx-preview-BbmDvXdS.js → docx-preview-m0bGH6IL.js} +1 -1
  15. package/dist/web/assets/{extension-webview-CP_AtfYs.js → extension-webview-D50XqcZE.js} +1 -1
  16. package/dist/web/assets/{git-log-panel-DPRoZgWG.js → git-log-panel-CyYHsqIb.js} +1 -1
  17. package/dist/web/assets/gitGraph-HDMCJU4V-BjUgCE-3.js +1 -0
  18. package/dist/web/assets/{glide-data-grid-BrtUKC3w.js → glide-data-grid-Dn8CvXh7.js} +1 -1
  19. package/dist/web/assets/{image-preview-BFj-ipom.js → image-preview-DTfGdGj8.js} +1 -1
  20. package/dist/web/assets/index-BgHCBGwE.js +27 -0
  21. package/dist/web/assets/info-3K5VOQVL-Bu3VpM9a.js +1 -0
  22. package/dist/web/assets/keybindings-store-BhM4ou6C.js +1 -0
  23. package/dist/web/assets/{markdown-renderer-B63eYfrn.js → markdown-renderer-940Y1Maq.js} +3 -3
  24. package/dist/web/assets/notification-store-DWsPQ2XR.js +1 -0
  25. package/dist/web/assets/packet-RMMSAZCW-C83Lg2yy.js +1 -0
  26. package/dist/web/assets/{pdf-preview-JOwOGTIk.js → pdf-preview-D8lVKDTs.js} +1 -1
  27. package/dist/web/assets/pie-UPGHQEXC-DoT-QQxi.js +1 -0
  28. package/dist/web/assets/{port-forwarding-tab-DJRRbLGF.js → port-forwarding-tab-B-hu3lGI.js} +1 -1
  29. package/dist/web/assets/{postgres-viewer-AIOBOfCg.js → postgres-viewer-DfCzELiK.js} +1 -1
  30. package/dist/web/assets/radar-KQ55EAFF-B6r4mqYF.js +1 -0
  31. package/dist/web/assets/{settings-store-CSDOihqv.js → settings-store-BFlBSwKg.js} +1 -1
  32. package/dist/web/assets/{settings-tab-BMHf9pO5.js → settings-tab-BNGqONuA.js} +1 -1
  33. package/dist/web/assets/{sql-query-editor-Dw9UvzWt.js → sql-query-editor-D3Jyjw6V.js} +1 -1
  34. package/dist/web/assets/{sqlite-viewer-HusTxs1Z.js → sqlite-viewer-B5Mrzox5.js} +1 -1
  35. package/dist/web/assets/{system-monitor-tab-BNJIkOan.js → system-monitor-tab-_ZjrnvgM.js} +1 -1
  36. package/dist/web/assets/{terminal-tab-W1VShnP7.js → terminal-tab-DNswsdLl.js} +1 -1
  37. package/dist/web/assets/treemap-KZPCXAKY-3t3gW0fB.js +1 -0
  38. package/dist/web/assets/{use-monaco-theme-qx6SfVRk.js → use-monaco-theme-6AirEH08.js} +1 -1
  39. package/dist/web/assets/{vendor-mermaid-DCie7hiR.js → vendor-mermaid-DU911Xa9.js} +2 -2
  40. package/dist/web/assets/{video-preview-BPAYbuvs.js → video-preview-CaB29_z0.js} +1 -1
  41. package/dist/web/index.html +3 -3
  42. package/dist/web/sw.js +1 -1
  43. package/package.json +1 -1
  44. package/src/index.ts +0 -0
  45. package/src/server/routes/projects.ts +37 -0
  46. package/src/services/git.service.ts +15 -0
  47. package/src/web/components/layout/add-project-form.tsx +213 -70
  48. package/.opencode/.env.example +0 -98
  49. package/.opencode/skills/ads-management/scripts/.env.example +0 -13
  50. package/.opencode/skills/ai-multimodal/.env.example +0 -230
  51. package/.opencode/skills/cip-design/.env.example +0 -6
  52. package/.opencode/skills/devops/.env.example +0 -76
  53. package/.opencode/skills/docs-seeker/.env.example +0 -15
  54. package/.opencode/skills/elevenlabs/.env.example +0 -3
  55. package/.opencode/skills/marketing-dashboard/.env.example +0 -15
  56. package/.opencode/skills/marketing-dashboard/app/.env.example +0 -2
  57. package/.opencode/skills/marketing-dashboard/server/.env.example +0 -2
  58. package/.opencode/skills/mcp-management/scripts/dist/analyze-tools.js +0 -70
  59. package/.opencode/skills/mcp-management/scripts/dist/cli.js +0 -160
  60. package/.opencode/skills/mcp-management/scripts/dist/mcp-client.js +0 -183
  61. package/.opencode/skills/payment-integration/scripts/.env.example +0 -20
  62. package/.opencode/skills/sequential-thinking/.env.example +0 -8
  63. package/dist/web/assets/architecture-PBZL5I3N-DLKD1Xjj.js +0 -1
  64. package/dist/web/assets/gitGraph-HDMCJU4V-2a0r4GHr.js +0 -1
  65. package/dist/web/assets/index-CJZZ6v1o.js +0 -27
  66. package/dist/web/assets/info-3K5VOQVL-CWKw4e0V.js +0 -1
  67. package/dist/web/assets/keybindings-store-BOV4khyp.js +0 -1
  68. package/dist/web/assets/notification-store-BklO85um.js +0 -1
  69. package/dist/web/assets/packet-RMMSAZCW-Ar00Wbhd.js +0 -1
  70. package/dist/web/assets/pie-UPGHQEXC-Q4ssDdib.js +0 -1
  71. package/dist/web/assets/radar-KQ55EAFF-kq5v4OKX.js +0 -1
  72. package/dist/web/assets/treemap-KZPCXAKY-DChODgHt.js +0 -1
@@ -20,17 +20,45 @@ interface AddProjectFormProps {
20
20
 
21
21
  export function AddProjectForm({ onSuccess, onCancel, footerClassName }: AddProjectFormProps) {
22
22
  const { addProject } = useProjectStore(useShallow((s) => ({ addProject: s.addProject })));
23
+
24
+ // ── Tab state ──────────────────────────────────────────────────────────────
25
+ const [tab, setTab] = useState<"local" | "clone">("local");
26
+
27
+ // ── Local folder state ─────────────────────────────────────────────────────
23
28
  const [path, setPath] = useState("");
24
29
  const [name, setName] = useState("");
25
30
  const [suggestions, setSuggestions] = useState<SuggestedDir[]>([]);
26
31
  const [showSuggestions, setShowSuggestions] = useState(false);
27
32
  const [loading, setLoading] = useState(false);
28
33
  const [submitting, setSubmitting] = useState(false);
34
+
35
+ // ── Clone state ────────────────────────────────────────────────────────────
36
+ const [gitUrl, setGitUrl] = useState("");
37
+ const [cloneDir, setCloneDir] = useState("~/Projects");
38
+ const [cloneName, setCloneName] = useState("");
39
+ const [cloning, setCloning] = useState(false);
40
+
41
+ // ── Shared ─────────────────────────────────────────────────────────────────
29
42
  const [error, setError] = useState("");
43
+
30
44
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
31
45
  const wrapperRef = useRef<HTMLDivElement>(null);
32
46
 
33
- // Fetch suggestions when path changes
47
+ // Load last clone dir on mount
48
+ useEffect(() => {
49
+ api.get<{ dir: string | null }>("/api/projects/last-clone-dir")
50
+ .then((res) => { if (res?.dir) setCloneDir(res.dir); })
51
+ .catch(() => {});
52
+ }, []);
53
+
54
+ // Auto-parse repo name from Git URL
55
+ useEffect(() => {
56
+ if (!gitUrl.trim()) { setCloneName(""); return; }
57
+ const match = gitUrl.match(/[/:]([^/:]+?)(?:\.git)?\s*$/);
58
+ if (match?.[1]) setCloneName(match[1]);
59
+ }, [gitUrl]);
60
+
61
+ // Fetch suggestions when local path changes
34
62
  useEffect(() => {
35
63
  if (debounceRef.current) clearTimeout(debounceRef.current);
36
64
  if (!path.trim()) { setSuggestions([]); setShowSuggestions(false); return; }
@@ -80,84 +108,199 @@ export function AddProjectForm({ onSuccess, onCancel, footerClassName }: AddProj
80
108
  }
81
109
  }
82
110
 
111
+ async function handleClone(e?: React.FormEvent) {
112
+ e?.preventDefault();
113
+ if (!gitUrl.trim()) { setError("Git URL is required"); return; }
114
+ if (!cloneDir.trim()) { setError("Clone directory is required"); return; }
115
+ setError("");
116
+ setCloning(true);
117
+ try {
118
+ const result = await api.post<{ path: string; project: unknown }>(
119
+ "/api/projects/git/clone",
120
+ { url: gitUrl.trim(), targetDir: cloneDir.trim(), name: cloneName.trim() || undefined },
121
+ );
122
+ // Server already added project — refresh store and select it
123
+ const { fetchProjects, setActiveProject } = useProjectStore.getState();
124
+ await fetchProjects();
125
+ const projects = useProjectStore.getState().projects;
126
+ const newProj = projects.find((p) => p.path === result.path);
127
+ if (newProj) setActiveProject(newProj);
128
+ onSuccess();
129
+ } catch (err) {
130
+ setError(err instanceof Error ? err.message : "Clone failed");
131
+ } finally {
132
+ setCloning(false);
133
+ }
134
+ }
135
+
83
136
  return (
84
- <form onSubmit={handleSubmit} className="flex flex-col gap-3">
85
- {/* Path input with suggestions */}
86
- <div ref={wrapperRef} className="relative">
87
- <label className="block text-xs font-medium text-foreground mb-1">Project path</label>
88
- <div className="flex gap-1.5 items-center">
89
- <div className="relative flex items-center flex-1">
90
- <FolderOpen className="absolute left-2.5 size-3.5 text-text-subtle pointer-events-none" />
137
+ <div className="flex flex-col gap-3">
138
+ {/* Tab bar */}
139
+ <div className="flex gap-1 border-b border-border">
140
+ {(["local", "clone"] as const).map((t) => (
141
+ <button
142
+ key={t}
143
+ type="button"
144
+ onClick={() => { setTab(t); setError(""); }}
145
+ className={cn(
146
+ "px-3 py-1.5 text-sm font-medium border-b-2 -mb-px transition-colors",
147
+ tab === t
148
+ ? "border-primary text-foreground"
149
+ : "border-transparent text-text-secondary hover:text-foreground",
150
+ )}
151
+ >
152
+ {t === "local" ? "Local folder" : "Clone from Git"}
153
+ </button>
154
+ ))}
155
+ </div>
156
+
157
+ {tab === "local" ? (
158
+ <form onSubmit={handleSubmit} className="flex flex-col gap-3">
159
+ {/* Path input with suggestions */}
160
+ <div ref={wrapperRef} className="relative">
161
+ <label className="block text-xs font-medium text-foreground mb-1">Project path</label>
162
+ <div className="flex gap-1.5 items-center">
163
+ <div className="relative flex items-center flex-1">
164
+ <FolderOpen className="absolute left-2.5 size-3.5 text-text-subtle pointer-events-none" />
165
+ <input
166
+ type="text"
167
+ value={path}
168
+ onChange={(e) => { setPath(e.target.value); setError(""); }}
169
+ onFocus={() => suggestions.length > 0 && setShowSuggestions(true)}
170
+ placeholder="/path/to/project"
171
+ className="w-full pl-8 pr-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
172
+ autoFocus
173
+ autoComplete="off"
174
+ />
175
+ {loading && <Loader2 className="absolute right-2.5 size-3.5 text-text-subtle animate-spin" />}
176
+ </div>
177
+ <BrowseButton
178
+ mode="folder"
179
+ onSelect={(selectedPath) => {
180
+ setPath(selectedPath);
181
+ if (!name) setName(selectedPath.split("/").pop() ?? "");
182
+ setError("");
183
+ }}
184
+ />
185
+ </div>
186
+
187
+ {/* Suggestions dropdown */}
188
+ {showSuggestions && suggestions.length > 0 && (
189
+ <div className="absolute top-full left-0 right-0 mt-1 z-10 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto">
190
+ {suggestions.map((dir) => (
191
+ <button
192
+ key={dir.path}
193
+ type="button"
194
+ onMouseDown={() => selectSuggestion(dir)}
195
+ className="w-full flex flex-col items-start px-3 py-2 text-left hover:bg-accent/50 transition-colors"
196
+ >
197
+ <span className="text-sm font-medium truncate w-full">{dir.name}</span>
198
+ <span className="text-xs text-text-subtle truncate w-full">{dir.path}</span>
199
+ </button>
200
+ ))}
201
+ </div>
202
+ )}
203
+ </div>
204
+
205
+ {/* Optional name */}
206
+ <div>
207
+ <label className="block text-xs font-medium text-foreground mb-1">Display name <span className="text-muted-foreground">(optional)</span></label>
208
+ <input
209
+ type="text"
210
+ value={name}
211
+ onChange={(e) => setName(e.target.value)}
212
+ placeholder="my-project"
213
+ className="w-full px-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
214
+ />
215
+ </div>
216
+
217
+ {error && <p className="text-xs text-destructive">{error}</p>}
218
+
219
+ <div className={cn("flex justify-end gap-2 pt-1", footerClassName)}>
220
+ <button
221
+ type="button"
222
+ onClick={onCancel}
223
+ className="px-3 py-1.5 text-sm text-text-secondary hover:text-foreground transition-colors"
224
+ >
225
+ Cancel
226
+ </button>
227
+ <button
228
+ type="submit"
229
+ disabled={submitting || !path.trim()}
230
+ className="px-3 py-1.5 text-sm bg-primary text-white rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50"
231
+ >
232
+ {submitting ? "Adding…" : "Add Project"}
233
+ </button>
234
+ </div>
235
+ </form>
236
+ ) : (
237
+ <form onSubmit={handleClone} className="flex flex-col gap-3">
238
+ {/* Git URL */}
239
+ <div>
240
+ <label className="block text-xs font-medium text-foreground mb-1">Git URL</label>
91
241
  <input
92
242
  type="text"
93
- value={path}
94
- onChange={(e) => { setPath(e.target.value); setError(""); }}
95
- onFocus={() => suggestions.length > 0 && setShowSuggestions(true)}
96
- placeholder="/path/to/project"
97
- className="w-full pl-8 pr-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
243
+ value={gitUrl}
244
+ onChange={(e) => { setGitUrl(e.target.value); setError(""); }}
245
+ placeholder="https://github.com/user/repo.git"
246
+ className="w-full px-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
98
247
  autoFocus
99
248
  autoComplete="off"
100
249
  />
101
- {loading && <Loader2 className="absolute right-2.5 size-3.5 text-text-subtle animate-spin" />}
102
250
  </div>
103
- <BrowseButton
104
- mode="folder"
105
- onSelect={(selectedPath) => {
106
- setPath(selectedPath);
107
- if (!name) setName(selectedPath.split("/").pop() ?? "");
108
- setError("");
109
- }}
110
- />
111
- </div>
112
-
113
- {/* Suggestions dropdown */}
114
- {showSuggestions && suggestions.length > 0 && (
115
- <div className="absolute top-full left-0 right-0 mt-1 z-10 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto">
116
- {suggestions.map((dir) => (
117
- <button
118
- key={dir.path}
119
- type="button"
120
- onMouseDown={() => selectSuggestion(dir)}
121
- className="w-full flex flex-col items-start px-3 py-2 text-left hover:bg-accent/50 transition-colors"
122
- >
123
- <span className="text-sm font-medium truncate w-full">{dir.name}</span>
124
- <span className="text-xs text-text-subtle truncate w-full">{dir.path}</span>
125
- </button>
126
- ))}
251
+
252
+ {/* Clone to directory */}
253
+ <div>
254
+ <label className="block text-xs font-medium text-foreground mb-1">Clone to</label>
255
+ <div className="flex gap-1.5 items-center">
256
+ <input
257
+ type="text"
258
+ value={cloneDir}
259
+ onChange={(e) => { setCloneDir(e.target.value); setError(""); }}
260
+ placeholder="~/Projects"
261
+ className="flex-1 px-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
262
+ />
263
+ <BrowseButton
264
+ mode="folder"
265
+ onSelect={(p) => { setCloneDir(p); setError(""); }}
266
+ />
267
+ </div>
127
268
  </div>
128
- )}
129
- </div>
130
269
 
131
- {/* Optional name */}
132
- <div>
133
- <label className="block text-xs font-medium text-foreground mb-1">Display name <span className="text-muted-foreground">(optional)</span></label>
134
- <input
135
- type="text"
136
- value={name}
137
- onChange={(e) => setName(e.target.value)}
138
- placeholder="my-project"
139
- className="w-full px-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
140
- />
141
- </div>
270
+ {/* Repo name override */}
271
+ <div>
272
+ <label className="block text-xs font-medium text-foreground mb-1">
273
+ Name <span className="text-muted-foreground">(auto-parsed from URL)</span>
274
+ </label>
275
+ <input
276
+ type="text"
277
+ value={cloneName}
278
+ onChange={(e) => setCloneName(e.target.value)}
279
+ placeholder="repo-name"
280
+ className="w-full px-3 py-2 rounded-md border border-border bg-background text-sm focus:outline-none focus:ring-1 focus:ring-primary"
281
+ />
282
+ </div>
142
283
 
143
- {error && <p className="text-xs text-destructive">{error}</p>}
144
-
145
- <div className={cn("flex justify-end gap-2 pt-1", footerClassName)}>
146
- <button
147
- type="button"
148
- onClick={onCancel}
149
- className="px-3 py-1.5 text-sm text-text-secondary hover:text-foreground transition-colors"
150
- >
151
- Cancel
152
- </button>
153
- <button
154
- type="submit"
155
- disabled={submitting || !path.trim()}
156
- className="px-3 py-1.5 text-sm bg-primary text-white rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50"
157
- >
158
- {submitting ? "Adding…" : "Add Project"}
159
- </button>
160
- </div>
161
- </form>
284
+ {error && <p className="text-xs text-destructive">{error}</p>}
285
+
286
+ <div className={cn("flex justify-end gap-2 pt-1", footerClassName)}>
287
+ <button
288
+ type="button"
289
+ onClick={onCancel}
290
+ className="px-3 py-1.5 text-sm text-text-secondary hover:text-foreground transition-colors"
291
+ >
292
+ Cancel
293
+ </button>
294
+ <button
295
+ type="submit"
296
+ disabled={cloning || !gitUrl.trim() || !cloneDir.trim()}
297
+ className="px-3 py-1.5 text-sm bg-primary text-white rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50"
298
+ >
299
+ {cloning ? <><Loader2 className="inline size-3.5 animate-spin mr-1.5" />Cloning…</> : "Clone & Add"}
300
+ </button>
301
+ </div>
302
+ </form>
303
+ )}
304
+ </div>
162
305
  );
163
306
  }
@@ -1,98 +0,0 @@
1
- # Claude Code - Global Environment Variables
2
- # Location: .claude/.env
3
- # Priority: LOWEST (overridden by skills/.env and skill-specific .env)
4
- # Scope: Project-wide configuration, global defaults
5
- # Setup: Copy to .claude/.env and configure
6
-
7
- # ============================================
8
- # Environment Variable Hierarchy
9
- # ============================================
10
- # Priority order (highest to lowest):
11
- # 1. process.env - Runtime environment (HIGHEST)
12
- # 2. .claude/skills/<skill>/.env - Skill-specific overrides
13
- # 3. .claude/skills/.env - Shared across all skills
14
- # 4. .claude/.env - Global defaults (this file, LOWEST)
15
- #
16
- # All skills use centralized resolver: ~/.claude/scripts/resolve_env.py
17
- # Debug hierarchy: python ~/.claude/scripts/resolve_env.py --show-hierarchy
18
-
19
- # ============================================
20
- # ClaudeKit API Key (for VidCap, ReviewWeb services)
21
- # ============================================
22
- # Get your API key from https://claudekit.cc/api-keys
23
- # Required for accessing ClaudeKit services via skills
24
- CLAUDEKIT_API_KEY=
25
-
26
- # ============================================
27
- # Claude Code Notification Hooks
28
- # ============================================
29
- # Discord Webhook URL (for Discord notifications)
30
- # Get from: Server Settings → Integrations → Webhooks → New Webhook
31
- DISCORD_WEBHOOK_URL=
32
-
33
- # Telegram Bot Token (for Telegram notifications)
34
- # Get from: @BotFather in Telegram
35
- TELEGRAM_BOT_TOKEN=
36
-
37
- # Telegram Chat ID (your chat ID or group ID)
38
- # Get from: https://api.telegram.org/bot<BOT_TOKEN>/getUpdates
39
- TELEGRAM_CHAT_ID=
40
-
41
- # ============================================
42
- # AI/ML API Keys (Global Defaults)
43
- # ============================================
44
- # Google Gemini API (for ai-multimodal, docs-seeker skills)
45
- # Get from: https://aistudio.google.com/apikey
46
- GEMINI_API_KEY=
47
-
48
- # Vertex AI Configuration (Optional alternative to AI Studio)
49
- # GEMINI_USE_VERTEX=true
50
- # VERTEX_PROJECT_ID=
51
- # VERTEX_LOCATION=us-central1
52
-
53
- # OpenAI API Key (if using OpenAI-based skills)
54
- # OPENAI_API_KEY=
55
-
56
- # Anthropic API Key (if using Claude API directly)
57
- # ANTHROPIC_API_KEY=
58
-
59
- # ElevenLabs API Key
60
- # Get your key at: https://elevenlabs.io/app/settings/api-keys
61
- # ELEVENLABS_API_KEY=
62
-
63
- # ============================================
64
- # Development & CI/CD
65
- # ============================================
66
- # NODE_ENV=development
67
- # DEBUG=false
68
- # LOG_LEVEL=info
69
-
70
- # ============================================
71
- # Project Configuration
72
- # ============================================
73
- # PROJECT_NAME=claudekit-engineer
74
- # ENVIRONMENT=local
75
-
76
- # ============================================
77
- # Example Usage Scenarios
78
- # ============================================
79
- # Scenario 1: Global default for all skills
80
- # .claude/.env (this file): GEMINI_API_KEY=global-dev-key
81
- # Result: All skills use global-dev-key
82
- #
83
- # Scenario 2: Override for all skills
84
- # .claude/.env (this file): GEMINI_API_KEY=global-dev-key
85
- # .claude/skills/.env: GEMINI_API_KEY=skills-prod-key
86
- # Result: All skills use skills-prod-key
87
- #
88
- # Scenario 3: Skill-specific override
89
- # .claude/.env (this file): GEMINI_API_KEY=global-key
90
- # .claude/skills/.env: GEMINI_API_KEY=shared-key
91
- # .claude/skills/ai-multimodal/.env: GEMINI_API_KEY=high-quota-key
92
- # Result: ai-multimodal uses high-quota-key, other skills use shared-key
93
- #
94
- # Scenario 4: Runtime testing
95
- # export GEMINI_API_KEY=test-key
96
- # Result: All skills use test-key regardless of config files
97
- #
98
- # Priority: runtime > skill-specific > shared > global (this file)
@@ -1,13 +0,0 @@
1
- # Google Ads API credentials
2
- GOOGLE_ADS_DEVELOPER_TOKEN=your_developer_token
3
- GOOGLE_ADS_CLIENT_ID=your_client_id.apps.googleusercontent.com
4
- GOOGLE_ADS_CLIENT_SECRET=your_client_secret
5
- GOOGLE_ADS_REFRESH_TOKEN=1//your_refresh_token
6
- GOOGLE_ADS_CUSTOMER_ID=1234567890
7
- GOOGLE_ADS_LOGIN_CUSTOMER_ID=your_mcc_id
8
-
9
- # Meta/Facebook Ads API credentials
10
- META_APP_ID=your_app_id
11
- META_APP_SECRET=your_app_secret
12
- META_ACCESS_TOKEN=your_system_user_token
13
- META_AD_ACCOUNT_ID=act_123456789
@@ -1,230 +0,0 @@
1
- # Google Gemini API Configuration
2
-
3
- # ============================================================================
4
- # OPTION 1: Google AI Studio (Default - Recommended for most users)
5
- # ============================================================================
6
- # Get your API key: https://aistudio.google.com/apikey
7
- GEMINI_API_KEY=your_api_key_here
8
-
9
- # ============================================================================
10
- # API Key Rotation (Optional - For high-volume usage)
11
- # ============================================================================
12
- # Add multiple API keys for automatic rotation on rate limit errors.
13
- # Free tier accounts are heavily rate-limited; rotation helps distribute load.
14
- #
15
- # Format: GEMINI_API_KEY_N where N is 2, 3, 4, etc.
16
- # The primary GEMINI_API_KEY is always used first.
17
- #
18
- # GEMINI_API_KEY_2=your_second_api_key
19
- # GEMINI_API_KEY_3=your_third_api_key
20
- # GEMINI_API_KEY_4=your_fourth_api_key
21
- #
22
- # Features:
23
- # - Auto-rotates on RESOURCE_EXHAUSTED / 429 errors
24
- # - 60-second cooldown per key after rate limit
25
- # - Logs rotation events with --verbose flag
26
- # - Backward compatible: single key still works
27
-
28
- # ============================================================================
29
- # OPTION 2: Vertex AI (Google Cloud Platform)
30
- # ============================================================================
31
- # Uncomment these lines to use Vertex AI instead of Google AI Studio
32
- # GEMINI_USE_VERTEX=true
33
- # VERTEX_PROJECT_ID=your-gcp-project-id
34
- # VERTEX_LOCATION=us-central1
35
-
36
- # ============================================================================
37
- # Model Selection (Optional)
38
- # ============================================================================
39
- # Override default models for specific capabilities
40
- # If not set, intelligent defaults are used based on task type
41
-
42
- # --- Image Generation ---
43
- # Used by: --task generate (image)
44
- # Default: gemini-2.5-flash-image (Nano Banana Flash - fast, cost-effective)
45
- # Alternative: imagen-4.0-generate-001 (production quality)
46
- # NOTE: All image generation requires billing - no free tier available (limit: 0)
47
- # Options:
48
- # gemini-2.5-flash-image - Nano Banana Flash: fast, ~$1/1M tokens (DEFAULT)
49
- # gemini-3-pro-image-preview - Nano Banana Pro: 4K text, reasoning (requires billing)
50
- # imagen-4.0-generate-001 - Imagen 4 Standard: production quality (~$0.02/image)
51
- # imagen-4.0-ultra-generate-001 - Imagen 4 Ultra: maximum quality (~$0.04/image)
52
- # imagen-4.0-fast-generate-001 - Imagen 4 Fast: speed-optimized (~$0.01/image)
53
- # IMAGE_GEN_MODEL=gemini-2.5-flash-image
54
-
55
- # --- Video Generation ---
56
- # Used by: --task generate-video (new capability)
57
- # Default: veo-3.1-generate-preview
58
- # NOTE: Video generation requires billing - no free tier fallback available
59
- # Options:
60
- # veo-3.1-generate-preview - Latest, native audio, frame control (requires billing)
61
- # veo-3.1-fast-generate-preview - Speed-optimized for business (requires billing)
62
- # veo-3.0-generate-001 - Stable, native audio, 8s videos (requires billing)
63
- # veo-3.0-fast-generate-001 - Stable fast variant (requires billing)
64
- # VIDEO_GEN_MODEL=veo-3.1-generate-preview
65
-
66
- # --- Multimodal Analysis ---
67
- # Used by: --task analyze, transcribe, extract
68
- # Default: gemini-2.5-flash
69
- # Options:
70
- # gemini-3-pro-preview - Latest, agentic workflows, 1M context
71
- # gemini-2.5-flash - Best price/performance (recommended)
72
- # gemini-2.5-pro - Highest quality
73
- # MULTIMODAL_MODEL=gemini-2.5-flash
74
-
75
- # --- Legacy Compatibility ---
76
- # Generic model override (use specific variables above instead)
77
- # GEMINI_MODEL=gemini-2.5-flash
78
- # GEMINI_IMAGE_GEN_MODEL=gemini-2.5-flash-image
79
-
80
- # ============================================================================
81
- # MiniMax API Configuration (Optional - for image/video/speech/music generation)
82
- # ============================================================================
83
- # Get your API key: https://platform.minimax.io/user-center/basic-information/interface-key
84
- # MINIMAX_API_KEY=your_minimax_api_key_here
85
-
86
- # --- MiniMax Image Generation ---
87
- # Models: image-01 (standard), image-01-live (enhanced)
88
- # Cost: ~$0.03/image | Rate: 10 RPM
89
- # MINIMAX_IMAGE_MODEL=image-01
90
-
91
- # --- MiniMax Video Generation (Hailuo) ---
92
- # Models: MiniMax-Hailuo-2.3, MiniMax-Hailuo-2.3-Fast, MiniMax-Hailuo-02, S2V-01
93
- # Cost: $0.25-0.52/video | Rate: 5 RPM
94
- # MINIMAX_VIDEO_MODEL=MiniMax-Hailuo-2.3
95
-
96
- # --- MiniMax Speech/TTS ---
97
- # Models: speech-2.8-hd (best), speech-2.8-turbo (fast)
98
- # Cost: $30-50/1M chars | Rate: 60 RPM | 300+ voices, 40+ languages
99
- # MINIMAX_SPEECH_MODEL=speech-2.8-hd
100
-
101
- # --- MiniMax Music Generation ---
102
- # Models: music-2.5 (4-minute songs with vocals)
103
- # Cost: $0.03-0.075/gen | Rate: 120 RPM
104
- # MINIMAX_MUSIC_MODEL=music-2.5
105
-
106
- # ============================================================================
107
- # Rate Limiting Configuration (Optional)
108
- # ============================================================================
109
- # Requests per minute limit (adjust based on your tier)
110
- # GEMINI_RPM_LIMIT=15
111
-
112
- # Tokens per minute limit
113
- # GEMINI_TPM_LIMIT=4000000
114
-
115
- # Requests per day limit
116
- # GEMINI_RPD_LIMIT=1500
117
-
118
- # ============================================================================
119
- # Video Generation Options (Optional)
120
- # ============================================================================
121
- # Video duration in seconds (8s only for now)
122
- # VEO_DURATION=8
123
-
124
- # Video resolution: 720p or 1080p
125
- # VEO_RESOLUTION=1080p
126
-
127
- # Aspect ratio: 16:9, 9:16, 1:1 (16:9 is default)
128
- # VEO_ASPECT_RATIO=16:9
129
-
130
- # Frame rate: 24fps (fixed for now)
131
- # VEO_FPS=24
132
-
133
- # Enable native audio generation
134
- # VEO_AUDIO=true
135
-
136
- # ============================================================================
137
- # Image Generation Options (Optional)
138
- # ============================================================================
139
- # Number of images to generate (1-4)
140
- # IMAGEN_NUM_IMAGES=1
141
-
142
- # Image size: 1K or 2K (Ultra/Standard only)
143
- # IMAGEN_SIZE=1K
144
-
145
- # Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4
146
- # IMAGEN_ASPECT_RATIO=1:1
147
-
148
- # Enable person generation (restricted in EEA, CH, UK)
149
- # IMAGEN_PERSON_GENERATION=true
150
-
151
- # Add SynthID watermark (always enabled by default)
152
- # IMAGEN_WATERMARK=true
153
-
154
- # ============================================================================
155
- # Processing Options (Optional)
156
- # ============================================================================
157
- # Video resolution mode: default or low-res
158
- # low-res uses ~100 tokens/second vs ~300 for default
159
- # GEMINI_VIDEO_RESOLUTION=default
160
-
161
- # Audio quality: default (16 Kbps mono, auto-downsampled)
162
- # GEMINI_AUDIO_QUALITY=default
163
-
164
- # PDF processing mode: inline (<20MB) or file-api (>20MB, automatic)
165
- # GEMINI_PDF_MODE=auto
166
-
167
- # ============================================================================
168
- # Retry Configuration (Optional)
169
- # ============================================================================
170
- # Maximum retry attempts for failed requests
171
- # GEMINI_MAX_RETRIES=3
172
-
173
- # Initial retry delay in seconds (uses exponential backoff)
174
- # GEMINI_RETRY_DELAY=1
175
-
176
- # ============================================================================
177
- # Output Configuration (Optional)
178
- # ============================================================================
179
- # Default output directory for generated images
180
- # OUTPUT_DIR=./output
181
-
182
- # Image output format (png or jpeg)
183
- # IMAGE_FORMAT=png
184
-
185
- # Image quality for JPEG (1-100)
186
- # IMAGE_QUALITY=95
187
-
188
- # ============================================================================
189
- # Context Caching (Optional)
190
- # ============================================================================
191
- # Enable context caching for repeated queries on same file
192
- # GEMINI_ENABLE_CACHING=true
193
-
194
- # Cache TTL in seconds (default: 1800 = 30 minutes)
195
- # GEMINI_CACHE_TTL=1800
196
-
197
- # ============================================================================
198
- # Logging (Optional)
199
- # ============================================================================
200
- # Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
201
- # LOG_LEVEL=INFO
202
-
203
- # Log file path
204
- # LOG_FILE=./logs/gemini.log
205
-
206
- # ============================================================================
207
- # Pricing Reference (as of 2025-11)
208
- # ============================================================================
209
- # Gemini 2.5 Flash: $1.00/1M input, $0.10/1M output
210
- # Gemini 2.5 Pro: $3.00/1M input, $12.00/1M output
211
- # Gemini 3 Pro: $2.00/1M input (<200k), $4.00 (>200k), $12/$18 output
212
- # Imagen 4: ~$0.01-$0.04 per image (varies by variant)
213
- # Veo 3: TBD (preview pricing)
214
- # Monitor: https://ai.google.dev/pricing
215
-
216
- # ============================================================================
217
- # Notes
218
- # ============================================================================
219
- # 1. Never commit API keys to version control
220
- # 2. Add .env to .gitignore
221
- # 3. API keys can be restricted in Google Cloud Console
222
- # 4. Monitor usage at: https://aistudio.google.com/apikey
223
- # 5. Free tier limits: 15 RPM, 1M-4M TPM, 1,500 RPD
224
- # 6. Vertex AI requires GCP authentication via gcloud CLI
225
- # 7. Model defaults (Dec 2025):
226
- # - Image gen: gemini-2.5-flash-image (Nano Banana Flash - default)
227
- # - Image gen: imagen-4.0-generate-001 (alternative for production)
228
- # - Video gen: veo-3.1-generate-preview
229
- # - Analysis: gemini-2.5-flash
230
- # 8. Preview models (veo-3.1, gemini-3) may have API changes
@@ -1,6 +0,0 @@
1
- # CIP Design Skill Environment Variables
2
- # Copy this file to .env and fill in your API key
3
-
4
- # Gemini API Key (required for image generation)
5
- # Get from: https://aistudio.google.com/apikey
6
- GEMINI_API_KEY=your-gemini-api-key-here