@amirhosseinnateghi/vibed-mcp 0.1.3 → 0.1.5

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 (2) hide show
  1. package/dist/index.js +210 -53
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1248,10 +1248,10 @@ var require_decode = __commonJS({
1248
1248
  this.state = EntityDecoderState.NumericDecimal;
1249
1249
  return this.stateNumericDecimal(str2, offset);
1250
1250
  };
1251
- EntityDecoder2.prototype.addToNumericResult = function(str2, start, end, base) {
1251
+ EntityDecoder2.prototype.addToNumericResult = function(str2, start, end, base2) {
1252
1252
  if (start !== end) {
1253
1253
  var digitCount = end - start;
1254
- this.result = this.result * Math.pow(base, digitCount) + parseInt(str2.substr(start, digitCount), base);
1254
+ this.result = this.result * Math.pow(base2, digitCount) + parseInt(str2.substr(start, digitCount), base2);
1255
1255
  this.consumed += digitCount;
1256
1256
  }
1257
1257
  };
@@ -6037,7 +6037,19 @@ var config = {
6037
6037
  // Light model that auto-tags the experience category. Falls back to a keyword
6038
6038
  // heuristic if the call fails.
6039
6039
  BUILDER_CLASSIFIER_MODEL: strEnv("BUILDER_CLASSIFIER_MODEL", "gemini-2.5-flash"),
6040
- BUILDER_MAX_TOKENS: intEnv("BUILDER_MAX_TOKENS", 8192),
6040
+ // Speech-to-text model for the builder's voice input (docs/builder-architecture.md).
6041
+ // Gemini 2.5 Flash transcribes the recorded clip via GapGPT's OpenAI-compatible
6042
+ // chat API (an `input_audio` content part) — verified to accept the exact
6043
+ // formats browsers produce (Chrome webm/opus, Safari mp4). It's multilingual,
6044
+ // so Persian and English speech both work with no per-language config.
6045
+ BUILDER_TRANSCRIBE_MODEL: strEnv("BUILDER_TRANSCRIBE_MODEL", "gemini-2.5-flash"),
6046
+ // Upload cap for a voice clip. A minute of webm/opus is ~250KB; 8MB is ample
6047
+ // headroom while still bounding what one request can hand the model.
6048
+ BUILDER_TRANSCRIBE_MAX_BYTES: intEnv("BUILDER_TRANSCRIBE_MAX_BYTES", 8388608),
6049
+ // Output budget per model call. 8k proved too small in prod — a rich game is
6050
+ // ~7-8k tokens of file alone, and a truncated file used to slip through as a
6051
+ // text dump. 24576 was probed OK against every catalog model on GapGPT.
6052
+ BUILDER_MAX_TOKENS: intEnv("BUILDER_MAX_TOKENS", 24576),
6041
6053
  BUILDER_MAX_MESSAGES: intEnv("BUILDER_MAX_MESSAGES", 30),
6042
6054
  // Hard cap for a single build turn (across the model + one repair). The build
6043
6055
  // runs detached from the request, so this — not the browser — is what stops a
@@ -9779,23 +9791,23 @@ var ZodEffects = class extends ZodType {
9779
9791
  }
9780
9792
  if (effect.type === "transform") {
9781
9793
  if (ctx.common.async === false) {
9782
- const base = this._def.schema._parseSync({
9794
+ const base2 = this._def.schema._parseSync({
9783
9795
  data: ctx.data,
9784
9796
  path: ctx.path,
9785
9797
  parent: ctx
9786
9798
  });
9787
- if (!isValid(base))
9799
+ if (!isValid(base2))
9788
9800
  return INVALID;
9789
- const result = effect.transform(base.value, checkCtx);
9801
+ const result = effect.transform(base2.value, checkCtx);
9790
9802
  if (result instanceof Promise) {
9791
9803
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
9792
9804
  }
9793
9805
  return { status: status.value, value: result };
9794
9806
  } else {
9795
- return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
9796
- if (!isValid(base))
9807
+ return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base2) => {
9808
+ if (!isValid(base2))
9797
9809
  return INVALID;
9798
- return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
9810
+ return Promise.resolve(effect.transform(base2.value, checkCtx)).then((result) => ({
9799
9811
  status: status.value,
9800
9812
  value: result
9801
9813
  }));
@@ -10190,8 +10202,13 @@ var STORAGE = {
10190
10202
  // 256 KB
10191
10203
  /** Max records returnable in one query page. */
10192
10204
  QUERY_MAX_LIMIT: intEnv("STORE_QUERY_MAX_LIMIT", 100),
10193
- /** Whether anonymous (logged-out) shared writes may be enabled per post. */
10194
- ANON_WRITES_AVAILABLE: boolEnv("STORE_ANON_WRITES_AVAILABLE", false)
10205
+ /**
10206
+ * Platform kill-switch for anonymous shared writes. ON by default: the real
10207
+ * gate is per-post owner opt-in (`anon_writes` + writePolicy `anyone`), so a
10208
+ * post only accepts guest writes when its owner turns them on. Set the env to
10209
+ * `false` to disable anon writes platform-wide regardless of per-post config.
10210
+ */
10211
+ ANON_WRITES_AVAILABLE: boolEnv("STORE_ANON_WRITES_AVAILABLE", true)
10195
10212
  };
10196
10213
  var COLLECTION_RE = /^[a-z0-9][a-z0-9:_-]{0,63}$/;
10197
10214
  var collectionSchemaStore = external_exports.string().regex(COLLECTION_RE, "invalid collection name");
@@ -10301,13 +10318,31 @@ var commentSchema = external_exports.object({
10301
10318
  parentId: external_exports.string().uuid().optional()
10302
10319
  });
10303
10320
  var eventSchema = external_exports.object({
10304
- kind: external_exports.enum(["impression", "play", "play_time", "share"]),
10321
+ // "record"/"screenshot" fire when a viewer captures the vibe (see the in-iframe
10322
+ // bridge capture flow, docs/architecture.md §5.4). Plain-text `viewEvents.kind`
10323
+ // column, so new kinds need no migration.
10324
+ kind: external_exports.enum(["impression", "play", "play_time", "share", "record", "screenshot"]),
10305
10325
  ms: external_exports.number().int().nonnegative().optional()
10306
10326
  });
10307
10327
  var createBuildSchema = external_exports.object({
10308
10328
  mode: external_exports.enum(["ai", "remix"]),
10309
10329
  remixParentId: external_exports.string().uuid().optional()
10310
10330
  });
10331
+ var turnAttachmentSchema = external_exports.object({
10332
+ kind: external_exports.enum(["image", "audio", "file", "sketch"]),
10333
+ key: external_exports.string().max(300).default(""),
10334
+ name: external_exports.string().max(200),
10335
+ // NUL bytes break Postgres jsonb (and mean binary-not-text) — strip defensively.
10336
+ text: external_exports.string().max(5e4).transform((t) => t.replace(/\u0000/g, "")).optional()
10337
+ });
10338
+ var createTurnRequestSchema = external_exports.object({
10339
+ clientTurnId: external_exports.string().uuid(),
10340
+ content: external_exports.string().max(4e3).default(""),
10341
+ attachments: external_exports.array(turnAttachmentSchema).max(6).optional(),
10342
+ model: external_exports.string().max(100).optional()
10343
+ }).refine((v) => v.content.trim().length > 0 || (v.attachments?.length ?? 0) > 0, {
10344
+ message: "Send a message or an attachment."
10345
+ });
10311
10346
  var builderAttachmentSchema = external_exports.object({
10312
10347
  kind: external_exports.enum(["image", "file", "audio"]),
10313
10348
  name: external_exports.string().max(200),
@@ -10408,27 +10443,52 @@ var notifPrefsPatchSchema = external_exports.object({
10408
10443
  groups: external_exports.record(external_exports.enum(NOTIF_PREF_GROUPS), external_exports.boolean()).optional()
10409
10444
  });
10410
10445
 
10411
- // ../shared/src/models.ts
10412
- var BUILDER_MODELS = [
10413
- // ── Free (selectable now) — ONLY genuinely strong builders ─────────
10414
- // Curated 2026-07: every id verified against GapGPT with a real full-HTML
10415
- // build (not a ping). Weak/cheap models were removed on purpose — a bad
10416
- // first build costs more in user trust than the tokens save.
10417
- { id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", tier: "free", note: "Best for polished vibes \xB7 fast", vision: true },
10418
- { id: "claude-sonnet-4-20250514", label: "Claude Sonnet 4", tier: "free", note: "Reliable all-rounder", vision: true },
10419
- { id: "gpt-5.2", label: "GPT-5.2", tier: "free", note: "Strong generalist", vision: true },
10420
- { id: "gpt-5.3-codex", label: "GPT-5.3 Codex", tier: "free", note: "Code specialist" },
10421
- { id: "gemini-2.5-pro", label: "Gemini 2.5 Pro", tier: "free", note: "Great UI sense", vision: true },
10422
- { id: "deepseek-v4-pro", label: "DeepSeek V4 Pro", tier: "free", note: "Strong coder \xB7 great value" },
10423
- { id: "qwen3-coder-480b-a35b-instruct", label: "Qwen3 Coder 480B", tier: "free", note: "Fast, capable coder" },
10424
- // ── Subscription (locked until billing is enabled) — premium ──────
10425
- // Opus 4.8 is owner-only early access: locked for everyone, live for the owner.
10426
- { id: "claude-opus-4-8", label: "Claude Opus 4.8", tier: "subscription", note: "Top quality", vision: true, ownerOnly: true },
10427
- { id: "gpt-5.2-pro", label: "GPT-5.2 Pro", tier: "subscription", note: "Premium" },
10428
- { id: "gemini-3-pro-preview", label: "Gemini 3 Pro", tier: "subscription", note: "Premium", vision: true }
10446
+ // ../shared/src/builderEvents.ts
10447
+ var BUILD_EVENT_VERSION = 1;
10448
+ var BUILD_PHASES = [
10449
+ "queued",
10450
+ // accepted, waiting for a worker slot
10451
+ "thinking",
10452
+ // model connected, no visible output yet (keep-alives flowing)
10453
+ "writing",
10454
+ // streaming the vibe's code/prose
10455
+ "validating",
10456
+ // sandbox pass on the produced artifact
10457
+ "repairing"
10458
+ // one automatic fix round after a validation failure
10429
10459
  ];
10430
- var FREE_IDS = new Set(BUILDER_MODELS.filter((m) => m.tier === "free").map((m) => m.id));
10431
- var OWNER_MODEL_IDS = new Set(BUILDER_MODELS.filter((m) => m.ownerOnly).map((m) => m.id));
10460
+ var base = { v: external_exports.literal(BUILD_EVENT_VERSION), turnId: external_exports.string() };
10461
+ var buildEventSchema = external_exports.discriminatedUnion("type", [
10462
+ /** Assistant prose delta (never contains the HTML code block). */
10463
+ external_exports.object({ ...base, type: external_exports.literal("token"), text: external_exports.string() }),
10464
+ /**
10465
+ * Honest heartbeat, ~every 2s while the turn runs: real elapsed ms, real
10466
+ * output-token count, and the executor's actual phase. The UI renders this
10467
+ * as a live counter — a number that moves because work is happening.
10468
+ */
10469
+ external_exports.object({
10470
+ ...base,
10471
+ type: external_exports.literal("progress"),
10472
+ phase: external_exports.enum(BUILD_PHASES),
10473
+ elapsedMs: external_exports.number(),
10474
+ tokensOut: external_exports.number()
10475
+ }),
10476
+ /** Friendly status chip ("index.html", "Fixing a couple of things…"). */
10477
+ external_exports.object({ ...base, type: external_exports.literal("status"), summary: external_exports.string() }),
10478
+ /** First-message category classification. */
10479
+ external_exports.object({ ...base, type: external_exports.literal("category"), category: external_exports.string() }),
10480
+ /** The validated draft landed. Preview loads it from the draft URL. */
10481
+ external_exports.object({ ...base, type: external_exports.literal("artifact"), artifactKey: external_exports.string() }),
10482
+ /** Terminal: failure with taxonomy code + human message. */
10483
+ external_exports.object({
10484
+ ...base,
10485
+ type: external_exports.literal("error"),
10486
+ code: external_exports.string(),
10487
+ message: external_exports.string()
10488
+ }),
10489
+ /** Terminal: success. */
10490
+ external_exports.object({ ...base, type: external_exports.literal("done") })
10491
+ ]);
10432
10492
 
10433
10493
  // ../shared/src/i18n.ts
10434
10494
  var en = {
@@ -10462,6 +10522,21 @@ var en = {
10462
10522
  "share.title": "Share",
10463
10523
  "share.copyLink": "Copy link",
10464
10524
  "share.copied": "Link copied",
10525
+ "capture.record": "Record",
10526
+ "capture.screenshot": "Screenshot",
10527
+ "capture.recording": "Recording",
10528
+ "capture.stop": "Stop",
10529
+ "capture.preparing": "Preparing\u2026",
10530
+ "capture.processing": "Finishing\u2026",
10531
+ "capture.preview.videoTitle": "Your clip",
10532
+ "capture.preview.imageTitle": "Your screenshot",
10533
+ "capture.save": "Save",
10534
+ "capture.shareVia": "Share via\u2026",
10535
+ "capture.retry": "Try again",
10536
+ "capture.error": "Capture failed \u2014 try again.",
10537
+ "capture.allowShare": "Allow \u201CShare this tab\u201D to record \u2014 it captures just the vibe.",
10538
+ "capture.pickTab": "Choose \u201CThis Tab\u201D so it records only the vibe.",
10539
+ "capture.unsupported": `This ${ITEM.one} can't be captured yet. Try again in a moment.`,
10465
10540
  "create.title": "Create",
10466
10541
  "create.buildWithAI": "Build with AI",
10467
10542
  "create.import": "Import",
@@ -10653,6 +10728,15 @@ function validate(html, opts) {
10653
10728
  errors.push({ code: "NOT_HTML", message: "Content is not parseable as HTML." });
10654
10729
  return { ok: false, errors, warnings };
10655
10730
  }
10731
+ const scriptOpens = (html.match(/<script\b/gi) ?? []).length;
10732
+ const scriptCloses = (html.match(/<\/script\s*>/gi) ?? []).length;
10733
+ if (scriptOpens > scriptCloses) {
10734
+ errors.push({
10735
+ code: "TRUNCATED_DOC",
10736
+ message: "The file ends mid-<script> \u2014 it looks cut off. Regenerate or paste the complete file."
10737
+ });
10738
+ return { ok: false, errors, warnings };
10739
+ }
10656
10740
  if (on("frames") && root.querySelector("iframe, frame, object, embed")) {
10657
10741
  errors.push({
10658
10742
  code: "FRAME_TAG",
@@ -11147,7 +11231,7 @@ async function apiFetch({ apiBase, token }, method, path, body) {
11147
11231
  import { homedir } from "node:os";
11148
11232
  import { join as join2 } from "node:path";
11149
11233
  import { mkdir, readFile as readFile2, writeFile, chmod } from "node:fs/promises";
11150
- var DEFAULT_API = "https://web-production-834c9.up.railway.app";
11234
+ var DEFAULT_API = "https://vibed.city";
11151
11235
  var dir = () => join2(homedir(), ".vibed");
11152
11236
  var file = () => join2(dir(), "config.json");
11153
11237
  async function readFileConfig() {
@@ -11256,22 +11340,38 @@ function normalizeCategory(input) {
11256
11340
  const match = CATEGORIES.find((c) => c.toLowerCase() === input.toLowerCase());
11257
11341
  return match ?? "Other";
11258
11342
  }
11259
- async function publishArtifact(client, html, meta, projectDir) {
11260
- const imported = await apiFetch(client, "POST", "/imports", { html });
11343
+ async function importDraft(client, html) {
11344
+ const r = await apiFetch(client, "POST", "/imports", { html });
11345
+ return { draftArtifactKey: r.draftArtifactKey, previewUrl: r.previewUrl };
11346
+ }
11347
+ async function publishDraft(client, draftArtifactKey, meta, html, projectDir) {
11261
11348
  const exp = await apiFetch(client, "POST", "/experiences", {
11262
- title: meta.title?.trim() || inferTitle(html, projectDir),
11349
+ title: meta.title?.trim() || (html ? inferTitle(html, projectDir ?? ".") : "Untitled"),
11263
11350
  caption: meta.caption ?? "",
11264
11351
  category: normalizeCategory(meta.category),
11265
11352
  tags: meta.tags ?? [],
11266
11353
  remixable: meta.remixable ?? true,
11267
11354
  visibility: meta.visibility ?? "public",
11268
- draftArtifactKey: imported.draftArtifactKey
11355
+ draftArtifactKey
11269
11356
  });
11270
11357
  return { id: exp.id, status: exp.status, url: `${client.apiBase}/e/${exp.id}` };
11271
11358
  }
11359
+ async function publishArtifact(client, html, meta, projectDir) {
11360
+ const { draftArtifactKey } = await importDraft(client, html);
11361
+ return publishDraft(client, draftArtifactKey, meta, html, projectDir);
11362
+ }
11363
+ function experienceIdFrom(input) {
11364
+ const m = input.match(/\/e\/([0-9a-f-]{36})/i);
11365
+ return (m?.[1] ?? input.trim()).toLowerCase();
11366
+ }
11367
+ async function deleteExperience(client, idOrUrl) {
11368
+ const id = experienceIdFrom(idOrUrl);
11369
+ await apiFetch(client, "DELETE", `/experiences/${id}`);
11370
+ return id;
11371
+ }
11272
11372
 
11273
11373
  // src/index.ts
11274
- var SERVER_INFO = { name: "vibed", version: "0.1.0" };
11374
+ var SERVER_INFO = { name: "vibed", version: "0.1.5" };
11275
11375
  var PROTOCOL_VERSION = "2024-11-05";
11276
11376
  var CATEGORIES2 = ["Game", "Invitation", "Story", "News", "Quiz", "Tool", "Art", "Other"];
11277
11377
  var pendingLogin = null;
@@ -11346,12 +11446,47 @@ After the user approves, call vibed_login_wait.`;
11346
11446
  }
11347
11447
  }
11348
11448
  },
11449
+ {
11450
+ name: "vibed_draft",
11451
+ description: "Bundle the project and upload it as a PRIVATE draft, returning a hosted, sandboxed PREVIEW link (\u2026/p/<id>/) and a draftKey. Publishes nothing \u2014 the link is in no feed, profile, or search; only someone with it can open it. Show the preview to the user, then call vibed_publish with the returned draftKey once they approve. Requires sign-in.",
11452
+ inputSchema: {
11453
+ type: "object",
11454
+ properties: {
11455
+ path: { type: "string", description: "Project directory (default: current directory)" },
11456
+ entry: { type: "string" },
11457
+ build: { type: "boolean" },
11458
+ api: { type: "string", description: "API base URL (default: production)" }
11459
+ }
11460
+ },
11461
+ run: async (a) => {
11462
+ const dir2 = resolve2(str(a.path, "."));
11463
+ const cfg = await loadConfig({ api: str(a.api) || void 0 });
11464
+ if (!cfg.token) return "Not signed in. Use vibed_login first.";
11465
+ const result = await bundleProject(dir2, {
11466
+ entry: str(a.entry) || void 0,
11467
+ build: bool(a.build, true),
11468
+ onLog: (m) => process.stderr.write(m + "\n")
11469
+ });
11470
+ if (!result.ok || !result.html) {
11471
+ return `No preview \u2014 the project isn't vibeable yet.
11472
+
11473
+ ${formatReport(result)}`;
11474
+ }
11475
+ const d = await importDraft({ apiBase: cfg.apiBase, token: cfg.token }, result.html);
11476
+ return `\u2713 Private preview (not published): ${d.previewUrl ?? "(no preview URL)"}
11477
+ Show it to the user. To publish it, call vibed_publish with draftKey: ${d.draftArtifactKey}`;
11478
+ }
11479
+ },
11349
11480
  {
11350
11481
  name: "vibed_publish",
11351
- description: "Bundle the project and publish it as a vibed Experience. Requires sign-in (vibed_whoami / vibed_login). Confirm the title and category with the user first \u2014 publishing is public. Returns the published URL, or the blockers if it isn't vibeable.",
11482
+ description: "Publish a vibed Experience. Pass draftKey (from vibed_draft) to promote an already-previewed draft \u2014 the recommended flow \u2014 or pass path to bundle + publish in one shot. Requires sign-in (vibed_whoami / vibed_login). Confirm the title and category with the user first \u2014 publishing is public. Returns the published URL, or the blockers if it isn't vibeable.",
11352
11483
  inputSchema: {
11353
11484
  type: "object",
11354
11485
  properties: {
11486
+ draftKey: {
11487
+ type: "string",
11488
+ description: "Promote this draft (from vibed_draft) instead of bundling path"
11489
+ },
11355
11490
  path: { type: "string", description: "Project directory (default: current directory)" },
11356
11491
  title: { type: "string", description: "Title (default: page <title>/<h1>/folder name)" },
11357
11492
  category: { type: "string", enum: CATEGORIES2, description: "Experience category" },
@@ -11365,9 +11500,23 @@ After the user approves, call vibed_login_wait.`;
11365
11500
  }
11366
11501
  },
11367
11502
  run: async (a) => {
11368
- const dir2 = resolve2(str(a.path, "."));
11369
11503
  const cfg = await loadConfig({ api: str(a.api) || void 0 });
11370
11504
  if (!cfg.token) return "Not signed in. Use vibed_login first.";
11505
+ const client = { apiBase: cfg.apiBase, token: cfg.token };
11506
+ const meta = {
11507
+ title: str(a.title) || void 0,
11508
+ category: str(a.category) || void 0,
11509
+ tags: Array.isArray(a.tags) ? a.tags : void 0,
11510
+ caption: str(a.caption) || void 0,
11511
+ remixable: bool(a.remixable, true),
11512
+ visibility: bool(a.unlisted, false) ? "unlisted" : "public"
11513
+ };
11514
+ const draftKey = str(a.draftKey);
11515
+ if (draftKey) {
11516
+ const outcome2 = await publishDraft(client, draftKey, meta);
11517
+ return `\u2713 Published (${outcome2.status}): ${outcome2.url}`;
11518
+ }
11519
+ const dir2 = resolve2(str(a.path, "."));
11371
11520
  const result = await bundleProject(dir2, {
11372
11521
  entry: str(a.entry) || void 0,
11373
11522
  build: bool(a.build, true),
@@ -11378,21 +11527,29 @@ After the user approves, call vibed_login_wait.`;
11378
11527
 
11379
11528
  ${formatReport(result)}`;
11380
11529
  }
11381
- const outcome = await publishArtifact(
11382
- { apiBase: cfg.apiBase, token: cfg.token },
11383
- result.html,
11384
- {
11385
- title: str(a.title) || void 0,
11386
- category: str(a.category) || void 0,
11387
- tags: Array.isArray(a.tags) ? a.tags : void 0,
11388
- caption: str(a.caption) || void 0,
11389
- remixable: bool(a.remixable, true),
11390
- visibility: bool(a.unlisted, false) ? "unlisted" : "public"
11391
- },
11392
- dir2
11393
- );
11530
+ const outcome = await publishArtifact(client, result.html, meta, dir2);
11394
11531
  return `\u2713 Published (${outcome.status}): ${outcome.url}`;
11395
11532
  }
11533
+ },
11534
+ {
11535
+ name: "vibed_delete",
11536
+ description: "Delete one of your vibed posts, by experience id or a \u2026/e/<id> share URL. Owner-only (the server rejects deleting someone else's post). Destructive \u2014 confirm the specific post with the user first. Requires sign-in.",
11537
+ inputSchema: {
11538
+ type: "object",
11539
+ properties: {
11540
+ target: { type: "string", description: "Experience id or https://\u2026/e/<id> URL" },
11541
+ api: { type: "string", description: "API base URL (default: production)" }
11542
+ },
11543
+ required: ["target"]
11544
+ },
11545
+ run: async (a) => {
11546
+ const target = str(a.target);
11547
+ if (!target) return "Provide the post's id or its \u2026/e/<id> URL.";
11548
+ const cfg = await loadConfig({ api: str(a.api) || void 0 });
11549
+ if (!cfg.token) return "Not signed in. Use vibed_login first.";
11550
+ const id = await deleteExperience({ apiBase: cfg.apiBase, token: cfg.token }, target);
11551
+ return `\u2713 Deleted post ${id}.`;
11552
+ }
11396
11553
  }
11397
11554
  ];
11398
11555
  function write(msg) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amirhosseinnateghi/vibed-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "vibed MCP server — make it vibed from any MCP client (Codex, Cursor, Gemini, …): check, login, and publish tools",
5
5
  "license": "MIT",
6
6
  "type": "module",