@poncho-ai/cli 0.6.0 → 0.6.2

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 (50) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/chunk-2TLKQG7R.js +5360 -0
  3. package/dist/chunk-3FY4LP2E.js +4981 -0
  4. package/dist/chunk-3WQANPEG.js +5086 -0
  5. package/dist/chunk-44DXWF6D.js +5450 -0
  6. package/dist/chunk-62G3MI43.js +5316 -0
  7. package/dist/chunk-6J2JICGH.js +5135 -0
  8. package/dist/chunk-6KLC6MWK.js +5357 -0
  9. package/dist/chunk-6LG2DUWF.js +5181 -0
  10. package/dist/chunk-ASAXSYEZ.js +5179 -0
  11. package/dist/chunk-B5B5LAR2.js +5181 -0
  12. package/dist/chunk-C7T4EJNQ.js +4934 -0
  13. package/dist/chunk-EPG7ZYDE.js +5452 -0
  14. package/dist/chunk-FMRRGTJX.js +5041 -0
  15. package/dist/chunk-HHMFEU26.js +5451 -0
  16. package/dist/chunk-JRJY6LUC.js +5178 -0
  17. package/dist/chunk-O7SJY7YQ.js +5177 -0
  18. package/dist/chunk-PYP4SKOI.js +5125 -0
  19. package/dist/chunk-VECWMU7E.js +5276 -0
  20. package/dist/chunk-XEBDWQI6.js +5178 -0
  21. package/dist/chunk-YW2D7Z22.js +5360 -0
  22. package/dist/chunk-YZXMEO2T.js +5177 -0
  23. package/dist/cli.js +1 -1
  24. package/dist/index.d.ts +18 -2
  25. package/dist/index.js +11 -1
  26. package/dist/run-interactive-ink-6EJ6Z5HE.js +494 -0
  27. package/dist/run-interactive-ink-72BHZB7Q.js +494 -0
  28. package/dist/run-interactive-ink-BNRIM52Y.js +494 -0
  29. package/dist/run-interactive-ink-BZNBOELJ.js +494 -0
  30. package/dist/run-interactive-ink-GODBXZF3.js +494 -0
  31. package/dist/run-interactive-ink-J4AISGNQ.js +494 -0
  32. package/dist/run-interactive-ink-K75SE2J2.js +494 -0
  33. package/dist/run-interactive-ink-M2XKKPIJ.js +494 -0
  34. package/dist/run-interactive-ink-MITWAF7L.js +494 -0
  35. package/dist/run-interactive-ink-NR5BRFUF.js +494 -0
  36. package/dist/run-interactive-ink-OGNG6UYE.js +494 -0
  37. package/dist/run-interactive-ink-P3VNJEXK.js +494 -0
  38. package/dist/run-interactive-ink-PHLW5YWV.js +494 -0
  39. package/dist/run-interactive-ink-PVU3XABN.js +494 -0
  40. package/dist/run-interactive-ink-SLSK7BY5.js +494 -0
  41. package/dist/run-interactive-ink-TRPYQYHG.js +494 -0
  42. package/dist/run-interactive-ink-U2RPRBIR.js +494 -0
  43. package/dist/run-interactive-ink-U2WTGZJ3.js +494 -0
  44. package/dist/run-interactive-ink-UHBFYNNB.js +494 -0
  45. package/dist/run-interactive-ink-XQDUN6OS.js +494 -0
  46. package/dist/run-interactive-ink-XUHSJCGH.js +494 -0
  47. package/package.json +1 -1
  48. package/src/index.ts +322 -27
  49. package/src/web-ui.ts +350 -33
  50. package/test/cli.test.ts +232 -1
package/test/cli.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
1
+ import { lstat, mkdir, mkdtemp, readFile, symlink, writeFile } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
@@ -151,11 +151,14 @@ vi.mock("@poncho-ai/harness", () => ({
151
151
 
152
152
  import {
153
153
  buildTarget,
154
+ copySkillsFromPackage,
154
155
  initProject,
156
+ listInstalledSkills,
155
157
  listTools,
156
158
  mcpAdd,
157
159
  mcpList,
158
160
  mcpRemove,
161
+ removeSkillsFromPackage,
159
162
  runTests,
160
163
  startDevServer,
161
164
  updateAgentGuidance,
@@ -278,6 +281,203 @@ describe("cli", () => {
278
281
  expect(intro).toBeUndefined();
279
282
  });
280
283
 
284
+ it("copies discovered skill folders into local skills directory", async () => {
285
+ const projectDir = join(tempDir, "copy-skills-agent");
286
+ const packageDir = join(tempDir, "mock-skill-package");
287
+ await mkdir(projectDir, { recursive: true });
288
+ await mkdir(join(projectDir, "skills"), { recursive: true });
289
+ await mkdir(join(packageDir, "alpha"), { recursive: true });
290
+ await mkdir(join(packageDir, "nested", "beta"), { recursive: true });
291
+ await mkdir(join(packageDir, "nested", "beta", "scripts"), { recursive: true });
292
+
293
+ await writeFile(
294
+ join(packageDir, "alpha", "SKILL.md"),
295
+ "---\nname: alpha\n---\nAlpha skill\n",
296
+ "utf8",
297
+ );
298
+ await writeFile(
299
+ join(packageDir, "nested", "beta", "SKILL.md"),
300
+ "---\nname: beta\n---\nBeta skill\n",
301
+ "utf8",
302
+ );
303
+ await writeFile(
304
+ join(packageDir, "nested", "beta", "scripts", "run.ts"),
305
+ "export default async function run() { return { ok: true }; }\n",
306
+ "utf8",
307
+ );
308
+
309
+ const copied = await copySkillsFromPackage(projectDir, packageDir);
310
+
311
+ expect(copied).toEqual([
312
+ "skills/mock-skill-package/alpha",
313
+ "skills/mock-skill-package/beta",
314
+ ]);
315
+ const alphaSkill = await readFile(
316
+ join(projectDir, "skills", "mock-skill-package", "alpha", "SKILL.md"),
317
+ "utf8",
318
+ );
319
+ const betaScript = await readFile(
320
+ join(projectDir, "skills", "mock-skill-package", "beta", "scripts", "run.ts"),
321
+ "utf8",
322
+ );
323
+ expect(alphaSkill).toContain("name: alpha");
324
+ expect(betaScript).toContain("export default async function run");
325
+ });
326
+
327
+ it("fails when skill destination already exists", async () => {
328
+ const projectDir = join(tempDir, "collision-agent");
329
+ const packageDir = join(tempDir, "collision-package");
330
+ await mkdir(join(projectDir, "skills", "collision-package", "alpha"), { recursive: true });
331
+ await mkdir(join(packageDir, "alpha"), { recursive: true });
332
+ await writeFile(
333
+ join(packageDir, "alpha", "SKILL.md"),
334
+ "---\nname: alpha\n---\nCollision skill\n",
335
+ "utf8",
336
+ );
337
+
338
+ await expect(copySkillsFromPackage(projectDir, packageDir)).rejects.toThrow(
339
+ /destination already exists/i,
340
+ );
341
+ });
342
+
343
+ it("copies a specific skill when --path style option is used", async () => {
344
+ const projectDir = join(tempDir, "single-skill-agent");
345
+ const packageDir = join(tempDir, "single-skill-package");
346
+ await mkdir(projectDir, { recursive: true });
347
+ await mkdir(join(packageDir, "alpha"), { recursive: true });
348
+ await mkdir(join(packageDir, "nested", "beta"), { recursive: true });
349
+ await writeFile(
350
+ join(packageDir, "alpha", "SKILL.md"),
351
+ "---\nname: alpha\n---\nAlpha skill\n",
352
+ "utf8",
353
+ );
354
+ await writeFile(
355
+ join(packageDir, "nested", "beta", "SKILL.md"),
356
+ "---\nname: beta\n---\nBeta skill\n",
357
+ "utf8",
358
+ );
359
+
360
+ const copied = await copySkillsFromPackage(projectDir, packageDir, { path: "nested/beta" });
361
+ expect(copied).toEqual(["skills/single-skill-package/beta"]);
362
+ await expect(
363
+ readFile(join(projectDir, "skills", "single-skill-package", "alpha", "SKILL.md"), "utf8"),
364
+ ).rejects.toThrow();
365
+ const betaSkill = await readFile(
366
+ join(projectDir, "skills", "single-skill-package", "beta", "SKILL.md"),
367
+ "utf8",
368
+ );
369
+ expect(betaSkill).toContain("name: beta");
370
+ });
371
+
372
+ it("removes all copied skills for a package in one call", async () => {
373
+ const projectDir = join(tempDir, "remove-skills-agent");
374
+ const packageDir = join(tempDir, "remove-skills-package");
375
+ await mkdir(join(projectDir, "skills", "remove-skills-package", "alpha"), { recursive: true });
376
+ await mkdir(join(projectDir, "skills", "remove-skills-package", "beta"), { recursive: true });
377
+ await writeFile(
378
+ join(projectDir, "skills", "remove-skills-package", "alpha", "SKILL.md"),
379
+ "alpha\n",
380
+ "utf8",
381
+ );
382
+ await writeFile(
383
+ join(projectDir, "skills", "remove-skills-package", "beta", "SKILL.md"),
384
+ "beta\n",
385
+ "utf8",
386
+ );
387
+
388
+ await mkdir(join(packageDir, "alpha"), { recursive: true });
389
+ await mkdir(join(packageDir, "nested", "beta"), { recursive: true });
390
+ await writeFile(
391
+ join(packageDir, "alpha", "SKILL.md"),
392
+ "---\nname: alpha\n---\nAlpha skill\n",
393
+ "utf8",
394
+ );
395
+ await writeFile(
396
+ join(packageDir, "nested", "beta", "SKILL.md"),
397
+ "---\nname: beta\n---\nBeta skill\n",
398
+ "utf8",
399
+ );
400
+
401
+ const result = await removeSkillsFromPackage(projectDir, packageDir);
402
+ expect(result.removed).toEqual(["skills/remove-skills-package"]);
403
+ expect(result.missing).toEqual([]);
404
+ await expect(lstat(join(projectDir, "skills", "remove-skills-package"))).rejects.toThrow();
405
+ });
406
+
407
+ it("removes a specific skill path from namespaced folder", async () => {
408
+ const projectDir = join(tempDir, "remove-single-skill-agent");
409
+ const packageDir = join(tempDir, "remove-single-skill-package");
410
+ await mkdir(
411
+ join(projectDir, "skills", "remove-single-skill-package", "alpha"),
412
+ { recursive: true },
413
+ );
414
+ await mkdir(
415
+ join(projectDir, "skills", "remove-single-skill-package", "beta"),
416
+ { recursive: true },
417
+ );
418
+ await writeFile(
419
+ join(projectDir, "skills", "remove-single-skill-package", "alpha", "SKILL.md"),
420
+ "alpha\n",
421
+ "utf8",
422
+ );
423
+ await writeFile(
424
+ join(projectDir, "skills", "remove-single-skill-package", "beta", "SKILL.md"),
425
+ "beta\n",
426
+ "utf8",
427
+ );
428
+ await mkdir(join(packageDir, "alpha"), { recursive: true });
429
+ await mkdir(join(packageDir, "nested", "beta"), { recursive: true });
430
+ await writeFile(join(packageDir, "alpha", "SKILL.md"), "---\nname: alpha\n---\n", "utf8");
431
+ await writeFile(
432
+ join(packageDir, "nested", "beta", "SKILL.md"),
433
+ "---\nname: beta\n---\n",
434
+ "utf8",
435
+ );
436
+
437
+ const result = await removeSkillsFromPackage(projectDir, packageDir, { path: "nested/beta" });
438
+ expect(result.removed).toEqual(["skills/remove-single-skill-package/beta"]);
439
+ await expect(
440
+ lstat(join(projectDir, "skills", "remove-single-skill-package", "beta")),
441
+ ).rejects.toThrow();
442
+ const alphaStillExists = await readFile(
443
+ join(projectDir, "skills", "remove-single-skill-package", "alpha", "SKILL.md"),
444
+ "utf8",
445
+ );
446
+ expect(alphaStillExists).toContain("alpha");
447
+ });
448
+
449
+ it("lists installed skills with and without source filter", async () => {
450
+ const projectDir = join(tempDir, "list-skills-agent");
451
+ await mkdir(join(projectDir, "skills", "agent-skills", "alpha"), { recursive: true });
452
+ await mkdir(join(projectDir, "skills", "agent-skills", "beta"), { recursive: true });
453
+ await mkdir(join(projectDir, "skills", "other-source", "gamma"), { recursive: true });
454
+ await writeFile(
455
+ join(projectDir, "skills", "agent-skills", "alpha", "SKILL.md"),
456
+ "---\nname: alpha\n---\n",
457
+ "utf8",
458
+ );
459
+ await writeFile(
460
+ join(projectDir, "skills", "agent-skills", "beta", "SKILL.md"),
461
+ "---\nname: beta\n---\n",
462
+ "utf8",
463
+ );
464
+ await writeFile(
465
+ join(projectDir, "skills", "other-source", "gamma", "SKILL.md"),
466
+ "---\nname: gamma\n---\n",
467
+ "utf8",
468
+ );
469
+
470
+ const all = await listInstalledSkills(projectDir);
471
+ const filtered = await listInstalledSkills(projectDir, "vercel-labs/agent-skills");
472
+
473
+ expect(all).toEqual([
474
+ "skills/agent-skills/alpha",
475
+ "skills/agent-skills/beta",
476
+ "skills/other-source/gamma",
477
+ ]);
478
+ expect(filtered).toEqual(["skills/agent-skills/alpha", "skills/agent-skills/beta"]);
479
+ });
480
+
281
481
  it("supports smoke flow init -> dev -> api conversation endpoint", async () => {
282
482
  await initProject("smoke-agent", { workingDir: tempDir });
283
483
  const projectDir = join(tempDir, "smoke-agent");
@@ -634,6 +834,37 @@ describe("cli", () => {
634
834
  expect(result.failed).toBe(0);
635
835
  });
636
836
 
837
+ it("materializes symlinked skills into vercel build output", async () => {
838
+ const projectDir = join(tempDir, "symlink-skill-agent");
839
+ await mkdir(projectDir, { recursive: true });
840
+ await writeFile(join(projectDir, "AGENT.md"), "---\nname: symlink-agent\n---\n", "utf8");
841
+ await mkdir(join(projectDir, "skills"), { recursive: true });
842
+
843
+ const sourceSkillDir = join(projectDir, ".agents", "skills", "linked-skill");
844
+ await mkdir(sourceSkillDir, { recursive: true });
845
+ await writeFile(
846
+ join(sourceSkillDir, "SKILL.md"),
847
+ "---\nname: linked-skill\n---\nLinked skill\n",
848
+ "utf8",
849
+ );
850
+
851
+ await symlink(sourceSkillDir, join(projectDir, "skills", "linked-skill"));
852
+ await buildTarget(projectDir, "vercel");
853
+
854
+ const builtSkillDir = join(
855
+ projectDir,
856
+ ".poncho-build",
857
+ "vercel",
858
+ "skills",
859
+ "linked-skill",
860
+ );
861
+ const builtSkillDirStat = await lstat(builtSkillDir);
862
+ const builtSkill = await readFile(join(builtSkillDir, "SKILL.md"), "utf8");
863
+
864
+ expect(builtSkillDirStat.isSymbolicLink()).toBe(false);
865
+ expect(builtSkill).toContain("name: linked-skill");
866
+ });
867
+
637
868
  it("seeds bearer token placeholders in env files when adding mcp auth", async () => {
638
869
  await initProject("mcp-env-seed-agent", { workingDir: tempDir });
639
870
  const projectDir = join(tempDir, "mcp-env-seed-agent");