@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.
- package/CHANGELOG.md +5 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +3 -1
- package/bun.lock +2170 -0
- package/bunfig.toml +2 -0
- package/dist/web/assets/architecture-PBZL5I3N-CS5Rvu_a.js +1 -0
- package/dist/web/assets/{audio-preview-Iq-XRBGw.js → audio-preview-DLz0dEw6.js} +1 -1
- package/dist/web/assets/{chat-tab-DkVXRD9e.js → chat-tab-B0L_i2ls.js} +3 -3
- package/dist/web/assets/{code-editor-M6wHw8AZ.js → code-editor-BEdecTYr.js} +2 -2
- package/dist/web/assets/{conflict-editor-D_8t44Wi.js → conflict-editor-CkUhGJsN.js} +1 -1
- package/dist/web/assets/{database-viewer-Cj5yCn4w.js → database-viewer-CRBrhKbW.js} +1 -1
- package/dist/web/assets/{diff-viewer-BgPv67fJ.js → diff-viewer-DcyIipTd.js} +1 -1
- package/dist/web/assets/{docx-preview-BbmDvXdS.js → docx-preview-m0bGH6IL.js} +1 -1
- package/dist/web/assets/{extension-webview-CP_AtfYs.js → extension-webview-D50XqcZE.js} +1 -1
- package/dist/web/assets/{git-log-panel-DPRoZgWG.js → git-log-panel-CyYHsqIb.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BjUgCE-3.js +1 -0
- package/dist/web/assets/{glide-data-grid-BrtUKC3w.js → glide-data-grid-Dn8CvXh7.js} +1 -1
- package/dist/web/assets/{image-preview-BFj-ipom.js → image-preview-DTfGdGj8.js} +1 -1
- package/dist/web/assets/index-BgHCBGwE.js +27 -0
- package/dist/web/assets/info-3K5VOQVL-Bu3VpM9a.js +1 -0
- package/dist/web/assets/keybindings-store-BhM4ou6C.js +1 -0
- package/dist/web/assets/{markdown-renderer-B63eYfrn.js → markdown-renderer-940Y1Maq.js} +3 -3
- package/dist/web/assets/notification-store-DWsPQ2XR.js +1 -0
- package/dist/web/assets/packet-RMMSAZCW-C83Lg2yy.js +1 -0
- package/dist/web/assets/{pdf-preview-JOwOGTIk.js → pdf-preview-D8lVKDTs.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DoT-QQxi.js +1 -0
- package/dist/web/assets/{port-forwarding-tab-DJRRbLGF.js → port-forwarding-tab-B-hu3lGI.js} +1 -1
- package/dist/web/assets/{postgres-viewer-AIOBOfCg.js → postgres-viewer-DfCzELiK.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-B6r4mqYF.js +1 -0
- package/dist/web/assets/{settings-store-CSDOihqv.js → settings-store-BFlBSwKg.js} +1 -1
- package/dist/web/assets/{settings-tab-BMHf9pO5.js → settings-tab-BNGqONuA.js} +1 -1
- package/dist/web/assets/{sql-query-editor-Dw9UvzWt.js → sql-query-editor-D3Jyjw6V.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-HusTxs1Z.js → sqlite-viewer-B5Mrzox5.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-BNJIkOan.js → system-monitor-tab-_ZjrnvgM.js} +1 -1
- package/dist/web/assets/{terminal-tab-W1VShnP7.js → terminal-tab-DNswsdLl.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-3t3gW0fB.js +1 -0
- package/dist/web/assets/{use-monaco-theme-qx6SfVRk.js → use-monaco-theme-6AirEH08.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-DCie7hiR.js → vendor-mermaid-DU911Xa9.js} +2 -2
- package/dist/web/assets/{video-preview-BPAYbuvs.js → video-preview-CaB29_z0.js} +1 -1
- package/dist/web/index.html +3 -3
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -0
- package/src/server/routes/projects.ts +37 -0
- package/src/services/git.service.ts +15 -0
- package/src/web/components/layout/add-project-form.tsx +213 -70
- package/.opencode/.env.example +0 -98
- package/.opencode/skills/ads-management/scripts/.env.example +0 -13
- package/.opencode/skills/ai-multimodal/.env.example +0 -230
- package/.opencode/skills/cip-design/.env.example +0 -6
- package/.opencode/skills/devops/.env.example +0 -76
- package/.opencode/skills/docs-seeker/.env.example +0 -15
- package/.opencode/skills/elevenlabs/.env.example +0 -3
- package/.opencode/skills/marketing-dashboard/.env.example +0 -15
- package/.opencode/skills/marketing-dashboard/app/.env.example +0 -2
- package/.opencode/skills/marketing-dashboard/server/.env.example +0 -2
- package/.opencode/skills/mcp-management/scripts/dist/analyze-tools.js +0 -70
- package/.opencode/skills/mcp-management/scripts/dist/cli.js +0 -160
- package/.opencode/skills/mcp-management/scripts/dist/mcp-client.js +0 -183
- package/.opencode/skills/payment-integration/scripts/.env.example +0 -20
- package/.opencode/skills/sequential-thinking/.env.example +0 -8
- package/dist/web/assets/architecture-PBZL5I3N-DLKD1Xjj.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-2a0r4GHr.js +0 -1
- package/dist/web/assets/index-CJZZ6v1o.js +0 -27
- package/dist/web/assets/info-3K5VOQVL-CWKw4e0V.js +0 -1
- package/dist/web/assets/keybindings-store-BOV4khyp.js +0 -1
- package/dist/web/assets/notification-store-BklO85um.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-Ar00Wbhd.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-Q4ssDdib.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-kq5v4OKX.js +0 -1
- 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
|
-
//
|
|
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
|
-
<
|
|
85
|
-
{/*
|
|
86
|
-
<div
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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={
|
|
94
|
-
onChange={(e) => {
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
}
|
package/.opencode/.env.example
DELETED
|
@@ -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
|