@editframe/create 0.43.0 → 0.45.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 (99) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +16 -28
  3. package/dist/index.js.map +1 -1
  4. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  5. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  6. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  7. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  8. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  9. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  10. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  11. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  12. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  13. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  15. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  16. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  17. package/dist/skills/editframe-composition/SKILL.md +169 -0
  18. package/dist/skills/editframe-composition/references/audio.md +483 -0
  19. package/dist/skills/editframe-composition/references/captions.md +844 -0
  20. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  21. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  22. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  23. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  24. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  25. package/dist/skills/editframe-composition/references/events.md +499 -0
  26. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  27. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  28. package/dist/skills/editframe-composition/references/image.md +241 -0
  29. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  30. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  31. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  32. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  33. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  34. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  35. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  36. package/dist/skills/editframe-composition/references/surface.md +329 -0
  37. package/dist/skills/editframe-composition/references/text.md +627 -0
  38. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  39. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  40. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  41. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  42. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  43. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  44. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  45. package/dist/skills/editframe-composition/references/video.md +506 -0
  46. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  47. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  48. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  49. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  50. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  51. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  52. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  53. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  54. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  55. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  56. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  58. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  59. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  60. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  61. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  62. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  63. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  64. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  65. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  66. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  68. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  70. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  71. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  72. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  73. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  74. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  75. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  76. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  77. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  78. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  79. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  80. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  81. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  82. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  83. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  84. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  85. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  86. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  87. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  88. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  89. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  90. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  91. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  92. package/dist/templates/html/AGENTS.md +13 -0
  93. package/dist/templates/react/AGENTS.md +13 -0
  94. package/dist/utils.js +15 -16
  95. package/dist/utils.js.map +1 -1
  96. package/package.json +2 -2
  97. package/tsdown.config.ts +4 -0
  98. package/dist/detectAgent.js +0 -89
  99. package/dist/detectAgent.js.map +0 -1
@@ -0,0 +1,457 @@
1
+ ---
2
+ title: Troubleshooting Webhooks
3
+ description: "Diagnose and fix common webhook issues: signature verification failures, delivery timeouts, retries, and missed events."
4
+ type: reference
5
+ nav:
6
+ parent: "Troubleshooting"
7
+ priority: 5
8
+ ---
9
+
10
+ # Troubleshooting Webhooks
11
+
12
+ Debug common webhook issues and understand webhook delivery behavior.
13
+
14
+ ## Signature Verification Failures
15
+
16
+ ### Symptom
17
+
18
+ All webhooks are rejected with 401 Unauthorized or signature verification fails.
19
+
20
+ ### Causes and Solutions
21
+
22
+ #### 1. Hashing Parsed JSON Instead of Raw Body
23
+
24
+ **Problem**: You're hashing `JSON.stringify(req.body)` instead of the raw request body.
25
+
26
+ **Why it fails**: JSON serialization order is not guaranteed. The signature was computed on the original body, which may have different key order.
27
+
28
+ **Solution**: Hash the raw body string as received:
29
+
30
+ ```typescript
31
+ // Wrong
32
+ const signature = crypto
33
+ .createHmac("sha256", secret)
34
+ .update(JSON.stringify(req.body))
35
+ .digest("hex");
36
+
37
+ // Right
38
+ const signature = crypto
39
+ .createHmac("sha256", secret)
40
+ .update(req.rawBody) // Raw body as string/buffer
41
+ .digest("hex");
42
+ ```
43
+
44
+ **Express setup**:
45
+ ```typescript
46
+ app.use(bodyParser.json({
47
+ verify: (req, res, buf) => {
48
+ (req as any).rawBody = buf.toString('utf8');
49
+ }
50
+ }));
51
+ ```
52
+
53
+ #### 2. Wrong Webhook Secret
54
+
55
+ **Problem**: Using the API key instead of the webhook secret.
56
+
57
+ **Solution**: Use the webhook secret (shown once during API key creation):
58
+
59
+ ```typescript
60
+ // Wrong - this is your API key
61
+ const secret = "ef_live_abc123...";
62
+
63
+ // Right - this is your webhook secret
64
+ const secret = "whsec_abc123..." || process.env.EDITFRAME_WEBHOOK_SECRET;
65
+ ```
66
+
67
+ If you lost your webhook secret, regenerate it in the dashboard.
68
+
69
+ #### 3. Secret Has Whitespace
70
+
71
+ **Problem**: Secret has extra spaces, tabs, or newlines.
72
+
73
+ **Solution**: Trim the secret:
74
+ ```typescript
75
+ const secret = process.env.EDITFRAME_WEBHOOK_SECRET!.trim();
76
+ ```
77
+
78
+ #### 4. Using Non-Timing-Safe Comparison
79
+
80
+ **Problem**: String comparison is vulnerable to timing attacks and may behave unexpectedly.
81
+
82
+ **Solution**: Use timing-safe comparison:
83
+ ```typescript
84
+ // Wrong
85
+ if (signature === expectedSignature) { }
86
+
87
+ // Right
88
+ import crypto from "node:crypto";
89
+ if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { }
90
+ ```
91
+
92
+ ### Debug Steps
93
+
94
+ 1. **Log both signatures**:
95
+ ```typescript
96
+ console.log("Received:", signature);
97
+ console.log("Expected:", expectedSignature);
98
+ console.log("Match:", signature === expectedSignature);
99
+ ```
100
+
101
+ 2. **Log the payload being hashed**:
102
+ ```typescript
103
+ console.log("Raw body:", req.rawBody);
104
+ console.log("Body length:", req.rawBody.length);
105
+ ```
106
+
107
+ 3. **Verify secret is correct**:
108
+ ```typescript
109
+ console.log("Secret length:", secret.length);
110
+ console.log("Secret starts with:", secret.substring(0, 10));
111
+ ```
112
+
113
+ 4. **Test with known payload**:
114
+ ```typescript
115
+ const testPayload = '{"topic":"webhook.test","data":{"id":"test"}}';
116
+ const testSignature = crypto
117
+ .createHmac("sha256", secret)
118
+ .update(testPayload)
119
+ .digest("hex");
120
+ console.log("Test signature:", testSignature);
121
+ ```
122
+
123
+ ## Timeout Errors
124
+
125
+ ### Symptom
126
+
127
+ Webhooks fail with timeout errors in delivery logs. Events show multiple retry attempts.
128
+
129
+ ### Cause
130
+
131
+ Your endpoint takes longer than 30 seconds to respond.
132
+
133
+ ### Solution
134
+
135
+ Respond with 200 OK immediately, then process the event asynchronously:
136
+
137
+ ```typescript
138
+ // Wrong - synchronous processing
139
+ app.post("/webhooks/editframe", async (req, res) => {
140
+ await verifySignature(req);
141
+ await processEvent(req.body); // Takes 60 seconds
142
+ res.status(200).send("OK"); // Times out!
143
+ });
144
+
145
+ // Right - asynchronous processing
146
+ app.post("/webhooks/editframe", async (req, res) => {
147
+ await verifySignature(req);
148
+
149
+ // Respond immediately
150
+ res.status(200).send("OK");
151
+
152
+ // Process in background
153
+ processEvent(req.body).catch(console.error);
154
+ });
155
+ ```
156
+
157
+ Use a job queue for reliability:
158
+
159
+ ```typescript
160
+ import { Queue } from "bull";
161
+
162
+ const webhookQueue = new Queue("webhooks");
163
+
164
+ app.post("/webhooks/editframe", async (req, res) => {
165
+ await verifySignature(req);
166
+
167
+ // Add to queue
168
+ await webhookQueue.add({
169
+ topic: req.body.topic,
170
+ data: req.body.data,
171
+ });
172
+
173
+ res.status(200).send("OK");
174
+ });
175
+
176
+ // Process jobs in background
177
+ webhookQueue.process(async (job) => {
178
+ await processEvent(job.data);
179
+ });
180
+ ```
181
+
182
+ ## Missed Webhooks
183
+
184
+ ### Symptom
185
+
186
+ Expected webhooks are not received.
187
+
188
+ ### Causes and Solutions
189
+
190
+ #### 1. Events Not Subscribed
191
+
192
+ **Problem**: Webhook events not configured on API key.
193
+
194
+ **Solution**: Update API key's webhook events:
195
+
196
+ ```typescript
197
+ // Check current configuration
198
+ const apiKey = await db
199
+ .selectFrom("identity.api_keys")
200
+ .where("id", "=", apiKeyId)
201
+ .select(["webhook_events", "webhook_url"])
202
+ .executeTakeFirst();
203
+
204
+ console.log("Subscribed events:", apiKey.webhook_events);
205
+ console.log("Webhook URL:", apiKey.webhook_url);
206
+
207
+ // Update events
208
+ await db
209
+ .updateTable("identity.api_keys")
210
+ .where("id", "=", apiKeyId)
211
+ .set({
212
+ webhook_events: ["render.completed", "render.failed", "file.ready"]
213
+ })
214
+ .execute();
215
+ ```
216
+
217
+ #### 2. Webhook URL Not Set
218
+
219
+ **Problem**: API key doesn't have a webhook URL configured.
220
+
221
+ **Solution**: Set the webhook URL in the dashboard or via API.
222
+
223
+ #### 3. Endpoint Returns Error
224
+
225
+ **Problem**: Your endpoint returns 4xx or 5xx status, causing Editframe to mark delivery as failed.
226
+
227
+ **Solution**: Fix endpoint errors. Check logs for error details.
228
+
229
+ #### 4. Firewall Blocking Requests
230
+
231
+ **Problem**: Firewall or load balancer blocks webhook requests.
232
+
233
+ **Solution**:
234
+ - Whitelist Editframe's IP ranges (check documentation)
235
+ - Verify endpoint is publicly accessible
236
+ - Test with `curl` from external server
237
+
238
+ ### Debug Steps
239
+
240
+ 1. **Check delivery logs** in the Editframe dashboard:
241
+ - Go to API key detail page
242
+ - View "Webhook Deliveries"
243
+ - Check status codes and response bodies
244
+
245
+ 2. **Verify webhook configuration**:
246
+ ```typescript
247
+ // Test endpoint is reachable
248
+ fetch("https://your-app.com/webhooks/editframe", {
249
+ method: "POST",
250
+ headers: { "Content-Type": "application/json" },
251
+ body: JSON.stringify({ test: true })
252
+ });
253
+ ```
254
+
255
+ 3. **Test with webhook.site**:
256
+ - Temporarily set webhook URL to webhook.site
257
+ - Trigger event
258
+ - Verify webhook is sent
259
+
260
+ ## Duplicate Webhooks
261
+
262
+ ### Symptom
263
+
264
+ Same event is processed multiple times.
265
+
266
+ ### Cause
267
+
268
+ Webhooks are retried on failure or timeout. Your endpoint may process the same event multiple times.
269
+
270
+ ### Solution
271
+
272
+ Implement idempotency:
273
+
274
+ ```typescript
275
+ const processedEvents = new Set<string>();
276
+
277
+ app.post("/webhooks/editframe", async (req, res) => {
278
+ await verifySignature(req);
279
+
280
+ const event = req.body;
281
+ const eventId = `${event.topic}:${event.data.id}`;
282
+
283
+ if (processedEvents.has(eventId)) {
284
+ console.log(`Duplicate webhook: ${eventId}`);
285
+ return res.status(200).send("OK"); // Still return 200
286
+ }
287
+
288
+ processedEvents.add(eventId);
289
+ res.status(200).send("OK");
290
+
291
+ await processEvent(event);
292
+ });
293
+ ```
294
+
295
+ For production, use a database:
296
+
297
+ ```typescript
298
+ async function isProcessed(eventId: string): Promise<boolean> {
299
+ const result = await db.query(
300
+ "SELECT 1 FROM processed_webhooks WHERE event_id = $1",
301
+ [eventId]
302
+ );
303
+ return result.rows.length > 0;
304
+ }
305
+
306
+ async function markProcessed(eventId: string): Promise<void> {
307
+ await db.query(
308
+ "INSERT INTO processed_webhooks (event_id, processed_at) VALUES ($1, NOW()) ON CONFLICT DO NOTHING",
309
+ [eventId]
310
+ );
311
+ }
312
+ ```
313
+
314
+ ## Retry Behavior
315
+
316
+ ### How Retries Work
317
+
318
+ When webhook delivery fails:
319
+ 1. **First attempt**: Immediate delivery
320
+ 2. **Second attempt**: 10 seconds later
321
+ 3. **Third attempt**: 10 seconds later
322
+ 4. **Max retries**: 3 attempts total
323
+ 5. **Timeout**: 30 seconds per attempt
324
+
325
+ After 3 failed attempts, the event is marked as failed and retries stop.
326
+
327
+ ### What Triggers Retries
328
+
329
+ Retries occur when:
330
+ - Endpoint returns 4xx or 5xx status code
331
+ - Request times out (>30 seconds)
332
+ - Network error (connection refused, DNS failure)
333
+
334
+ Retries **do not** occur when:
335
+ - Endpoint returns 200 OK (even if processing fails)
336
+
337
+ ### Viewing Retry History
338
+
339
+ Check webhook delivery logs in the dashboard:
340
+ - Each attempt is logged with timestamp
341
+ - See status code and response for each attempt
342
+ - Failed events show number of retries
343
+
344
+ ### Handling Retries in Your Endpoint
345
+
346
+ ```typescript
347
+ app.post("/webhooks/editframe", async (req, res) => {
348
+ try {
349
+ await verifySignature(req);
350
+
351
+ // Respond immediately
352
+ res.status(200).send("OK");
353
+
354
+ // Process asynchronously
355
+ await processEvent(req.body);
356
+ } catch (error) {
357
+ console.error("Webhook processing error:", error);
358
+
359
+ // Still return 200 to prevent retries
360
+ // Log error for manual investigation
361
+ res.status(200).send("OK");
362
+ }
363
+ });
364
+ ```
365
+
366
+ **Important**: If you return an error status code, the webhook will be retried. Only return errors for transient failures that should be retried (e.g., database connection lost).
367
+
368
+ ## Debugging Checklist
369
+
370
+ When webhooks aren't working:
371
+
372
+ 1. **Verify configuration**:
373
+ - [ ] Webhook URL is correct
374
+ - [ ] Webhook URL uses HTTPS
375
+ - [ ] Webhook events are selected
376
+ - [ ] Endpoint is publicly accessible
377
+
378
+ 2. **Test signature verification**:
379
+ - [ ] Using correct webhook secret (not API key)
380
+ - [ ] Hashing raw body (not parsed JSON)
381
+ - [ ] Using timing-safe comparison
382
+ - [ ] Secret has no extra whitespace
383
+
384
+ 3. **Check endpoint behavior**:
385
+ - [ ] Returns 200 OK within 30 seconds
386
+ - [ ] Handles all subscribed event types
387
+ - [ ] Implements idempotency
388
+ - [ ] Logs errors for debugging
389
+
390
+ 4. **Review delivery logs**:
391
+ - [ ] Check status codes
392
+ - [ ] Review response bodies
393
+ - [ ] Count retry attempts
394
+ - [ ] Look for patterns in failures
395
+
396
+ 5. **Test locally**:
397
+ - [ ] Use ngrok to expose local server
398
+ - [ ] Send test webhook from dashboard
399
+ - [ ] Run integration tests
400
+ - [ ] Test with manual script
401
+
402
+ ## Getting Help
403
+
404
+ If you're still experiencing issues:
405
+
406
+ 1. **Check delivery logs** in the dashboard for detailed error messages
407
+ 2. **Test with webhook.site** to isolate the issue
408
+ 3. **Review webhook event payloads** in the [events.md](references/events.md) reference
409
+ 4. **Contact support** with:
410
+ - API key ID
411
+ - Webhook event ID (from delivery logs)
412
+ - Error messages from your logs
413
+ - Steps to reproduce
414
+
415
+ ## Common Error Messages
416
+
417
+ ### "Invalid signature"
418
+
419
+ **Cause**: Signature verification failed
420
+
421
+ **Solution**: See [Signature Verification Failures](#signature-verification-failures)
422
+
423
+ ### "Webhook URL is not set"
424
+
425
+ **Cause**: API key doesn't have webhook URL configured
426
+
427
+ **Solution**: Set webhook URL in API key configuration
428
+
429
+ ### "Connection refused"
430
+
431
+ **Cause**: Endpoint is not reachable
432
+
433
+ **Solution**:
434
+ - Verify endpoint is running
435
+ - Check firewall rules
436
+ - Test with `curl` from external server
437
+
438
+ ### "SSL certificate verify failed"
439
+
440
+ **Cause**: Endpoint uses invalid SSL certificate
441
+
442
+ **Solution**:
443
+ - Use a valid SSL certificate from a trusted CA
444
+ - For development, use ngrok which provides valid certificates
445
+
446
+ ### "Timed out after 30000ms"
447
+
448
+ **Cause**: Endpoint took longer than 30 seconds to respond
449
+
450
+ **Solution**: See [Timeout Errors](#timeout-errors)
451
+
452
+ ## Next Steps
453
+
454
+ - [security.md](references/security.md) — Review signature verification
455
+ - [testing.md](references/testing.md) — Test webhook endpoints
456
+ - [getting-started.md](references/getting-started.md) — Complete setup guide
457
+ - [events.md](references/events.md) — Webhook event reference
@@ -0,0 +1,13 @@
1
+ # Editframe Project
2
+
3
+ This is an Editframe video composition project. Use the skills in `.claude/skills/` or `.agents/skills/` for guidance.
4
+
5
+ ## Agent Workflow
6
+
7
+ - To render video: `npx editframe render src/index.js` — do not run `npm start`
8
+ - Edit existing files in `src/` — do not scaffold a new project
9
+ - Video compositions are HTML files; use `<ef-*>` web components or React equivalents
10
+
11
+ ## Docs
12
+
13
+ https://editframe.com/docs
@@ -0,0 +1,13 @@
1
+ # Editframe Project
2
+
3
+ This is an Editframe video composition project. Use the skills in `.claude/skills/` or `.agents/skills/` for guidance.
4
+
5
+ ## Agent Workflow
6
+
7
+ - To render video: `npx editframe render src/index.js` — do not run `npm start`
8
+ - Edit existing files in `src/` — do not scaffold a new project
9
+ - Video compositions are HTML files; use `<ef-*>` web components or React equivalents
10
+
11
+ ## Docs
12
+
13
+ https://editframe.com/docs
package/dist/utils.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { cp, mkdir, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
1
4
  import chalk from "chalk";
2
5
  import { execa } from "execa";
3
6
 
@@ -45,27 +48,23 @@ async function installDependencies(projectDir) {
45
48
  }
46
49
  }
47
50
  /**
48
- * Install AI agent skills using the ai-agent-skills CLI.
51
+ * Install AI agent skills by copying bundled skill files into the project.
52
+ * Writes to both .claude/skills/ and .agents/skills/ for broad agent compatibility.
49
53
  */
50
- async function installAgentSkills(projectDir, agent) {
54
+ async function installAgentSkills(projectDir) {
51
55
  try {
52
- process.stderr.write(chalk.bold(`\nInstalling AI agent skills for ${agent}...\n\n`));
53
- await execa("npx", [
54
- "ai-agent-skills",
55
- "install",
56
- "editframe/skills",
57
- ...agent === "all" ? [] : ["--agent", agent]
58
- ], {
59
- cwd: projectDir,
60
- stdout: "inherit",
61
- stderr: "inherit"
62
- });
63
- process.stderr.write(chalk.green(`\n✓ Agent skills installed for ${agent}!\n`));
56
+ process.stderr.write(chalk.bold("\nInstalling AI agent skills...\n\n"));
57
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
58
+ const skillsSource = path.join(__dirname, "skills");
59
+ const skills = await readdir(skillsSource);
60
+ for (const destBase of [".claude/skills", ".agents/skills"]) {
61
+ await mkdir(path.join(projectDir, destBase), { recursive: true });
62
+ for (const skill of skills) await cp(path.join(skillsSource, skill), path.join(projectDir, destBase, skill), { recursive: true });
63
+ }
64
+ process.stderr.write(chalk.green("\n✓ AI agent skills installed!\n"));
64
65
  return true;
65
66
  } catch (error) {
66
67
  process.stderr.write(chalk.yellow("\n⚠ Failed to install agent skills\n"));
67
- process.stderr.write(chalk.dim("You can install manually:\n"));
68
- process.stderr.write(chalk.cyan(` npx ai-agent-skills install editframe/skills --agent ${agent}\n\n`));
69
68
  return false;
70
69
  }
71
70
  }
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { execa } from \"execa\";\nimport chalk from \"chalk\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\n/**\n * Detect which package manager was used to invoke the create script.\n * Uses the npm_config_user_agent environment variable set by package managers.\n */\nexport function getUserPkgManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent;\n\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n if (userAgent.startsWith(\"bun\")) return \"bun\";\n }\n\n return \"npm\"; // Default fallback\n}\n\n/**\n * Run the appropriate install command for the detected package manager.\n * Shows full output to the user so they can see progress.\n */\nasync function runInstallCommand(\n pkgManager: PackageManager,\n projectDir: string,\n): Promise<void> {\n // Show all output directly to user - no hiding behind spinners\n await execa(pkgManager, [\"install\"], {\n cwd: projectDir,\n stdout: \"inherit\",\n stderr: \"inherit\",\n });\n}\n\n/**\n * Install dependencies in the project directory.\n */\nexport async function installDependencies(\n projectDir: string,\n): Promise<boolean> {\n const pkgManager = getUserPkgManager();\n\n try {\n process.stderr.write(\n chalk.bold(`\\nInstalling dependencies with ${pkgManager}...\\n\\n`),\n );\n\n await runInstallCommand(pkgManager, projectDir);\n\n process.stderr.write(\n chalk.green(\"\\n✓ Dependencies installed successfully!\\n\"),\n );\n\n return true;\n } catch (error) {\n process.stderr.write(chalk.yellow(\"\\n⚠ Dependency installation failed\\n\"));\n process.stderr.write(chalk.dim(\"You can install manually:\\n\"));\n process.stderr.write(chalk.cyan(` cd ${projectDir.split(\"/\").pop()}\\n`));\n process.stderr.write(chalk.cyan(` ${pkgManager} install\\n\\n`));\n return false;\n }\n}\n\n/**\n * Install AI agent skills using the ai-agent-skills CLI.\n */\nexport async function installAgentSkills(\n projectDir: string,\n agent: string,\n): Promise<boolean> {\n try {\n process.stderr.write(\n chalk.bold(`\\nInstalling AI agent skills for ${agent}...\\n\\n`),\n );\n\n const agentFlag = agent === \"all\" ? [] : [\"--agent\", agent];\n\n await execa(\n \"npx\",\n [\"ai-agent-skills\", \"install\", \"editframe/skills\", ...agentFlag],\n {\n cwd: projectDir,\n stdout: \"inherit\",\n stderr: \"inherit\",\n },\n );\n\n process.stderr.write(\n chalk.green(`\\n✓ Agent skills installed for ${agent}!\\n`),\n );\n return true;\n } catch (error) {\n process.stderr.write(chalk.yellow(\"\\n⚠ Failed to install agent skills\\n\"));\n process.stderr.write(chalk.dim(\"You can install manually:\\n\"));\n process.stderr.write(\n chalk.cyan(\n ` npx ai-agent-skills install editframe/skills --agent ${agent}\\n\\n`,\n ),\n );\n return false;\n }\n}\n\n/**\n * Get the appropriate start command for the package manager.\n */\nexport function getStartCommand(pkgManager: PackageManager): string {\n return pkgManager === \"npm\" ? \"npm start\" : `${pkgManager} start`;\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,oBAAoC;CAClD,MAAM,YAAY,QAAQ,IAAI;AAE9B,KAAI,WAAW;AACb,MAAI,UAAU,WAAW,OAAO,CAAE,QAAO;AACzC,MAAI,UAAU,WAAW,OAAO,CAAE,QAAO;AACzC,MAAI,UAAU,WAAW,MAAM,CAAE,QAAO;;AAG1C,QAAO;;;;;;AAOT,eAAe,kBACb,YACA,YACe;AAEf,OAAM,MAAM,YAAY,CAAC,UAAU,EAAE;EACnC,KAAK;EACL,QAAQ;EACR,QAAQ;EACT,CAAC;;;;;AAMJ,eAAsB,oBACpB,YACkB;CAClB,MAAM,aAAa,mBAAmB;AAEtC,KAAI;AACF,UAAQ,OAAO,MACb,MAAM,KAAK,kCAAkC,WAAW,SAAS,CAClE;AAED,QAAM,kBAAkB,YAAY,WAAW;AAE/C,UAAQ,OAAO,MACb,MAAM,MAAM,6CAA6C,CAC1D;AAED,SAAO;UACA,OAAO;AACd,UAAQ,OAAO,MAAM,MAAM,OAAO,uCAAuC,CAAC;AAC1E,UAAQ,OAAO,MAAM,MAAM,IAAI,8BAA8B,CAAC;AAC9D,UAAQ,OAAO,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;AACzE,UAAQ,OAAO,MAAM,MAAM,KAAK,KAAK,WAAW,cAAc,CAAC;AAC/D,SAAO;;;;;;AAOX,eAAsB,mBACpB,YACA,OACkB;AAClB,KAAI;AACF,UAAQ,OAAO,MACb,MAAM,KAAK,oCAAoC,MAAM,SAAS,CAC/D;AAID,QAAM,MACJ,OACA;GAAC;GAAmB;GAAW;GAAoB,GAJnC,UAAU,QAAQ,EAAE,GAAG,CAAC,WAAW,MAAM;GAIO,EAChE;GACE,KAAK;GACL,QAAQ;GACR,QAAQ;GACT,CACF;AAED,UAAQ,OAAO,MACb,MAAM,MAAM,kCAAkC,MAAM,KAAK,CAC1D;AACD,SAAO;UACA,OAAO;AACd,UAAQ,OAAO,MAAM,MAAM,OAAO,uCAAuC,CAAC;AAC1E,UAAQ,OAAO,MAAM,MAAM,IAAI,8BAA8B,CAAC;AAC9D,UAAQ,OAAO,MACb,MAAM,KACJ,0DAA0D,MAAM,MACjE,CACF;AACD,SAAO;;;;;;AAOX,SAAgB,gBAAgB,YAAoC;AAClE,QAAO,eAAe,QAAQ,cAAc,GAAG,WAAW"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { cp, mkdir, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execa } from \"execa\";\nimport chalk from \"chalk\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\n/**\n * Detect which package manager was used to invoke the create script.\n * Uses the npm_config_user_agent environment variable set by package managers.\n */\nexport function getUserPkgManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent;\n\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n if (userAgent.startsWith(\"bun\")) return \"bun\";\n }\n\n return \"npm\"; // Default fallback\n}\n\n/**\n * Run the appropriate install command for the detected package manager.\n * Shows full output to the user so they can see progress.\n */\nasync function runInstallCommand(\n pkgManager: PackageManager,\n projectDir: string,\n): Promise<void> {\n // Show all output directly to user - no hiding behind spinners\n await execa(pkgManager, [\"install\"], {\n cwd: projectDir,\n stdout: \"inherit\",\n stderr: \"inherit\",\n });\n}\n\n/**\n * Install dependencies in the project directory.\n */\nexport async function installDependencies(\n projectDir: string,\n): Promise<boolean> {\n const pkgManager = getUserPkgManager();\n\n try {\n process.stderr.write(\n chalk.bold(`\\nInstalling dependencies with ${pkgManager}...\\n\\n`),\n );\n\n await runInstallCommand(pkgManager, projectDir);\n\n process.stderr.write(\n chalk.green(\"\\n✓ Dependencies installed successfully!\\n\"),\n );\n\n return true;\n } catch (error) {\n process.stderr.write(chalk.yellow(\"\\n⚠ Dependency installation failed\\n\"));\n process.stderr.write(chalk.dim(\"You can install manually:\\n\"));\n process.stderr.write(chalk.cyan(` cd ${projectDir.split(\"/\").pop()}\\n`));\n process.stderr.write(chalk.cyan(` ${pkgManager} install\\n\\n`));\n return false;\n }\n}\n\n/**\n * Install AI agent skills by copying bundled skill files into the project.\n * Writes to both .claude/skills/ and .agents/skills/ for broad agent compatibility.\n */\nexport async function installAgentSkills(projectDir: string): Promise<boolean> {\n try {\n process.stderr.write(chalk.bold(\"\\nInstalling AI agent skills...\\n\\n\"));\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const skillsSource = path.join(__dirname, \"skills\");\n const skills = await readdir(skillsSource);\n\n for (const destBase of [\".claude/skills\", \".agents/skills\"]) {\n await mkdir(path.join(projectDir, destBase), { recursive: true });\n for (const skill of skills) {\n await cp(\n path.join(skillsSource, skill),\n path.join(projectDir, destBase, skill),\n { recursive: true },\n );\n }\n }\n\n process.stderr.write(chalk.green(\"\\n✓ AI agent skills installed!\\n\"));\n return true;\n } catch (error) {\n process.stderr.write(chalk.yellow(\"\\n⚠ Failed to install agent skills\\n\"));\n return false;\n }\n}\n\n/**\n * Get the appropriate start command for the package manager.\n */\nexport function getStartCommand(pkgManager: PackageManager): string {\n return pkgManager === \"npm\" ? \"npm start\" : `${pkgManager} start`;\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,oBAAoC;CAClD,MAAM,YAAY,QAAQ,IAAI;AAE9B,KAAI,WAAW;AACb,MAAI,UAAU,WAAW,OAAO,CAAE,QAAO;AACzC,MAAI,UAAU,WAAW,OAAO,CAAE,QAAO;AACzC,MAAI,UAAU,WAAW,MAAM,CAAE,QAAO;;AAG1C,QAAO;;;;;;AAOT,eAAe,kBACb,YACA,YACe;AAEf,OAAM,MAAM,YAAY,CAAC,UAAU,EAAE;EACnC,KAAK;EACL,QAAQ;EACR,QAAQ;EACT,CAAC;;;;;AAMJ,eAAsB,oBACpB,YACkB;CAClB,MAAM,aAAa,mBAAmB;AAEtC,KAAI;AACF,UAAQ,OAAO,MACb,MAAM,KAAK,kCAAkC,WAAW,SAAS,CAClE;AAED,QAAM,kBAAkB,YAAY,WAAW;AAE/C,UAAQ,OAAO,MACb,MAAM,MAAM,6CAA6C,CAC1D;AAED,SAAO;UACA,OAAO;AACd,UAAQ,OAAO,MAAM,MAAM,OAAO,uCAAuC,CAAC;AAC1E,UAAQ,OAAO,MAAM,MAAM,IAAI,8BAA8B,CAAC;AAC9D,UAAQ,OAAO,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;AACzE,UAAQ,OAAO,MAAM,MAAM,KAAK,KAAK,WAAW,cAAc,CAAC;AAC/D,SAAO;;;;;;;AAQX,eAAsB,mBAAmB,YAAsC;AAC7E,KAAI;AACF,UAAQ,OAAO,MAAM,MAAM,KAAK,sCAAsC,CAAC;EAEvE,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;EAC9D,MAAM,eAAe,KAAK,KAAK,WAAW,SAAS;EACnD,MAAM,SAAS,MAAM,QAAQ,aAAa;AAE1C,OAAK,MAAM,YAAY,CAAC,kBAAkB,iBAAiB,EAAE;AAC3D,SAAM,MAAM,KAAK,KAAK,YAAY,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjE,QAAK,MAAM,SAAS,OAClB,OAAM,GACJ,KAAK,KAAK,cAAc,MAAM,EAC9B,KAAK,KAAK,YAAY,UAAU,MAAM,EACtC,EAAE,WAAW,MAAM,CACpB;;AAIL,UAAQ,OAAO,MAAM,MAAM,MAAM,mCAAmC,CAAC;AACrE,SAAO;UACA,OAAO;AACd,UAAQ,OAAO,MAAM,MAAM,OAAO,uCAAuC,CAAC;AAC1E,SAAO;;;;;;AAOX,SAAgB,gBAAgB,YAAoC;AAClE,QAAO,eAAe,QAAQ,cAAc,GAAG,WAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/create",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "create-editframe": "dist/index.js"
@@ -18,7 +18,7 @@
18
18
  "build:watch": "tsdown --watch"
19
19
  },
20
20
  "author": "",
21
- "license": "UNLICENSED",
21
+ "license": "SEE LICENSE IN LICENSE-FULL.md",
22
22
  "dependencies": {
23
23
  "chalk": "^5.3.0",
24
24
  "prompts": "^2.4.2",
package/tsdown.config.ts CHANGED
@@ -15,6 +15,10 @@ export default defineConfig(
15
15
  from: "src/templates",
16
16
  to: "dist/templates",
17
17
  },
18
+ {
19
+ from: "src/skills",
20
+ to: "dist/skills",
21
+ },
18
22
  ],
19
23
  hooks: {
20
24
  async onSuccess() {
@@ -1,89 +0,0 @@
1
- import { access } from "node:fs/promises";
2
- import path from "node:path";
3
- import os from "node:os";
4
-
5
- //#region src/detectAgent.ts
6
- /**
7
- * Detect which AI coding agents are installed on the system.
8
- * Returns an array of detected agent names.
9
- */
10
- async function detectInstalledAgents() {
11
- const detected = [];
12
- try {
13
- await access(path.join(process.cwd(), ".cursor"));
14
- detected.push("cursor");
15
- } catch {}
16
- try {
17
- await access(path.join(process.cwd(), ".vscode"));
18
- detected.push("vscode");
19
- } catch {
20
- try {
21
- await access(path.join(process.cwd(), ".github/copilot"));
22
- detected.push("vscode");
23
- } catch {}
24
- }
25
- try {
26
- await access(path.join(os.homedir(), ".claude"));
27
- detected.push("claude");
28
- } catch {}
29
- try {
30
- await access(path.join(process.cwd(), ".opencode"));
31
- detected.push("opencode");
32
- } catch {
33
- try {
34
- await access(path.join(process.cwd(), "AGENTS.md"));
35
- detected.push("opencode");
36
- } catch {}
37
- }
38
- try {
39
- await access(path.join(os.homedir(), ".windsurf"));
40
- detected.push("windsurf");
41
- } catch {}
42
- return detected;
43
- }
44
- /**
45
- * Get agent choices sorted by detection (detected agents first, then others).
46
- */
47
- async function getAgentChoices() {
48
- const detected = await detectInstalledAgents();
49
- return [
50
- {
51
- title: "Cursor",
52
- value: "cursor"
53
- },
54
- {
55
- title: "VS Code Copilot",
56
- value: "vscode"
57
- },
58
- {
59
- title: "Claude Code",
60
- value: "claude"
61
- },
62
- {
63
- title: "OpenCode",
64
- value: "opencode"
65
- },
66
- {
67
- title: "Windsurf",
68
- value: "windsurf"
69
- },
70
- {
71
- title: "All agents",
72
- value: "all"
73
- },
74
- {
75
- title: "Skip",
76
- value: "skip"
77
- }
78
- ].sort((a, b) => {
79
- const aDetected = detected.includes(a.value);
80
- const bDetected = detected.includes(b.value);
81
- if (aDetected && !bDetected) return -1;
82
- if (!aDetected && bDetected) return 1;
83
- return 0;
84
- });
85
- }
86
-
87
- //#endregion
88
- export { getAgentChoices };
89
- //# sourceMappingURL=detectAgent.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"detectAgent.js","names":["detected: string[]"],"sources":["../src/detectAgent.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\n/**\n * Detect which AI coding agents are installed on the system.\n * Returns an array of detected agent names.\n */\nexport async function detectInstalledAgents(): Promise<string[]> {\n const detected: string[] = [];\n\n // Check for Cursor (project-level .cursor directory)\n try {\n await access(path.join(process.cwd(), \".cursor\"));\n detected.push(\"cursor\");\n } catch {\n // Not found\n }\n\n // Check for VS Code / Copilot (.vscode directory or .github/copilot)\n try {\n await access(path.join(process.cwd(), \".vscode\"));\n detected.push(\"vscode\");\n } catch {\n try {\n await access(path.join(process.cwd(), \".github/copilot\"));\n detected.push(\"vscode\");\n } catch {\n // Not found\n }\n }\n\n // Check for Claude Code (user-level ~/.claude directory)\n try {\n await access(path.join(os.homedir(), \".claude\"));\n detected.push(\"claude\");\n } catch {\n // Not found\n }\n\n // Check for OpenCode (project-level .opencode directory or AGENTS.md)\n try {\n await access(path.join(process.cwd(), \".opencode\"));\n detected.push(\"opencode\");\n } catch {\n try {\n await access(path.join(process.cwd(), \"AGENTS.md\"));\n detected.push(\"opencode\");\n } catch {\n // Not found\n }\n }\n\n // Check for Windsurf (user-level ~/.windsurf directory)\n try {\n await access(path.join(os.homedir(), \".windsurf\"));\n detected.push(\"windsurf\");\n } catch {\n // Not found\n }\n\n return detected;\n}\n\n/**\n * Get agent choices sorted by detection (detected agents first, then others).\n */\nexport async function getAgentChoices() {\n const detected = await detectInstalledAgents();\n\n // Define all available agents\n const allAgents = [\n { title: \"Cursor\", value: \"cursor\" },\n { title: \"VS Code Copilot\", value: \"vscode\" },\n { title: \"Claude Code\", value: \"claude\" },\n { title: \"OpenCode\", value: \"opencode\" },\n { title: \"Windsurf\", value: \"windsurf\" },\n { title: \"All agents\", value: \"all\" },\n { title: \"Skip\", value: \"skip\" },\n ];\n\n // Sort: detected agents first, then others\n const sorted = allAgents.sort((a, b) => {\n const aDetected = detected.includes(a.value);\n const bDetected = detected.includes(b.value);\n\n if (aDetected && !bDetected) return -1;\n if (!aDetected && bDetected) return 1;\n return 0;\n });\n\n return sorted;\n}\n"],"mappings":";;;;;;;;;AAQA,eAAsB,wBAA2C;CAC/D,MAAMA,WAAqB,EAAE;AAG7B,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,CAAC;AACjD,WAAS,KAAK,SAAS;SACjB;AAKR,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,CAAC;AACjD,WAAS,KAAK,SAAS;SACjB;AACN,MAAI;AACF,SAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,kBAAkB,CAAC;AACzD,YAAS,KAAK,SAAS;UACjB;;AAMV,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,GAAG,SAAS,EAAE,UAAU,CAAC;AAChD,WAAS,KAAK,SAAS;SACjB;AAKR,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,YAAY,CAAC;AACnD,WAAS,KAAK,WAAW;SACnB;AACN,MAAI;AACF,SAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,YAAY,CAAC;AACnD,YAAS,KAAK,WAAW;UACnB;;AAMV,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,GAAG,SAAS,EAAE,YAAY,CAAC;AAClD,WAAS,KAAK,WAAW;SACnB;AAIR,QAAO;;;;;AAMT,eAAsB,kBAAkB;CACtC,MAAM,WAAW,MAAM,uBAAuB;AAuB9C,QApBkB;EAChB;GAAE,OAAO;GAAU,OAAO;GAAU;EACpC;GAAE,OAAO;GAAmB,OAAO;GAAU;EAC7C;GAAE,OAAO;GAAe,OAAO;GAAU;EACzC;GAAE,OAAO;GAAY,OAAO;GAAY;EACxC;GAAE,OAAO;GAAY,OAAO;GAAY;EACxC;GAAE,OAAO;GAAc,OAAO;GAAO;EACrC;GAAE,OAAO;GAAQ,OAAO;GAAQ;EACjC,CAGwB,MAAM,GAAG,MAAM;EACtC,MAAM,YAAY,SAAS,SAAS,EAAE,MAAM;EAC5C,MAAM,YAAY,SAAS,SAAS,EAAE,MAAM;AAE5C,MAAI,aAAa,CAAC,UAAW,QAAO;AACpC,MAAI,CAAC,aAAa,UAAW,QAAO;AACpC,SAAO;GACP"}