plyo-mcp 0.2.0 → 0.3.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 (2) hide show
  1. package/dist/index.js +25 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -109,13 +109,26 @@ async function pushBranch(branch) {
109
109
  await git(["push", "--force", REMOTE_NAME, `HEAD:${branch}`]);
110
110
  return;
111
111
  }
112
- throw new Error(`The project already has saved work that this folder doesn't have. Bring it in first with: git pull ${REMOTE_NAME} ${branch} --rebase — then save again. (Never force-push; the user's timeline lives there.)`);
112
+ // The project moved on Plyo's side (a restore, an upload, a teammate).
113
+ // Rebase this folder's work on top and push once more — the common case
114
+ // resolves itself; only a real conflict surfaces to the agent.
115
+ try {
116
+ await git(["pull", REMOTE_NAME, branch, "--rebase"]);
117
+ await git(["push", REMOTE_NAME, `HEAD:${branch}`]);
118
+ return;
119
+ }
120
+ catch {
121
+ await git(["rebase", "--abort"]).catch(() => "");
122
+ throw new Error(`The project has newer saved work that clashes with this folder's changes. Run: git pull ${REMOTE_NAME} ${branch} --rebase — resolve what git reports, then save again. (Never force-push; the user's timeline lives there.)`);
123
+ }
113
124
  }
114
125
  }
115
126
  function text(s) {
116
127
  return { content: [{ type: "text", text: s }] };
117
128
  }
118
- const server = new McpServer({ name: "plyo", version: "0.1.0" });
129
+ const server = new McpServer({ name: "plyo", version: "0.3.0" }, {
130
+ instructions: "Plyo keeps every version of this project safe for a non-technical user. Call save_checkpoint automatically after every completed piece of work — do not wait to be asked. Pass both a plain-English note and the intent (what the user asked for, in their words). If the user started a Draft in Plyo, use start_draft with that draft's exact name to work in it — never create a duplicate.",
131
+ });
119
132
  server.tool("list_projects", "List the user's Plyo projects (name, slug, live URL).", {}, async () => {
120
133
  const data = await api("/v1/projects");
121
134
  if (!data.projects.length)
@@ -162,8 +175,17 @@ server.tool("save_checkpoint", "Save the current state of the project as a check
162
175
  await pushBranch(target);
163
176
  return text(`Checkpoint saved: "${note}". It's on the user's timeline and can be restored anytime.`);
164
177
  });
165
- server.tool("start_draft", "Start a Draft — a safe copy of the project to try changes without touching the live version. Switches this folder to the draft.", { name: z.string().describe("What this draft tries, e.g. 'Trying a new pricing page'") }, async ({ name }) => {
178
+ server.tool("start_draft", "Start a Draft — a safe copy of the project to try changes without touching the live version. Switches this folder to the draft. If the user already started a draft in Plyo, pass its exact name and this folder joins it instead of creating a new one.", { name: z.string().describe("What this draft tries, e.g. 'Trying a new pricing page'") }, async ({ name }) => {
166
179
  const link = await requireLink();
180
+ // The user may have started this draft in the dashboard already — join it
181
+ // instead of creating a same-named twin they'd find empty.
182
+ const existing = await api(`/v1/projects/${link.slug}/drafts`);
183
+ const match = existing.drafts.find((d) => d.status === "open" && d.name.trim().toLowerCase() === name.trim().toLowerCase());
184
+ if (match?.branch) {
185
+ await git(["fetch", REMOTE_NAME, match.branch]);
186
+ await git(["checkout", "-B", match.branch, `${REMOTE_NAME}/${match.branch}`]);
187
+ return text(`Joined the user's existing draft "${match.name}" — this folder now works in it. save_checkpoint saves into the draft, and it shows up on the draft's page in Plyo.`);
188
+ }
167
189
  const data = await api(`/v1/projects/${link.slug}/drafts`, { method: "POST", body: JSON.stringify({ name }) });
168
190
  const branch = data.hint.match(/"([^"]+)"/)?.[1];
169
191
  if (branch) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plyo-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for Plyo \u2014 Undo and Publish for everything your AI builds. Save checkpoints, publish, restore.",
5
5
  "type": "module",
6
6
  "bin": {