@juicedresume/mcp 0.2.1 → 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 +50 -5
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -228,6 +228,11 @@ var Styling = z.object({
228
228
  // em x 1000 (we treat as px-ish)
229
229
  headingsLine: z.enum(["none", "underline", "left-bar", "boxed"]).default("none"),
230
230
  spacing: z.enum(["compact", "regular", "spacious"]).default("regular"),
231
+ // Fine-grained, MS-Word-style spacing control (px @96dpi). Drive the gap
232
+ // between sections and between items within a section. Defaults match the
233
+ // previous hardcoded single-column values (mb-4 = 16px, ~10px item gaps).
234
+ sectionSpacing: z.number().default(16),
235
+ itemSpacing: z.number().default(10),
231
236
  bulletGlyph: z.enum(["disc", "dash", "arrow", "square", "check", "none"]).default("disc"),
232
237
  layout: z.enum(["single", "sidebar-left", "sidebar-right", "two-column"]).default("single"),
233
238
  subtitlePlacement: z.enum(["below", "inline", "right"]).default("below"),
@@ -237,9 +242,9 @@ var Styling = z.object({
237
242
  photoSize: z.number().default(72),
238
243
  // Page margins in px @96dpi (used by the preview slicer + PDF export).
239
244
  // Defaults match the previous hardcoded values: ~0.5in top/bottom, ~0.6in sides.
240
- pageMarginX: z.number().default(56),
241
- pageMarginTop: z.number().default(48),
242
- pageMarginBottom: z.number().default(48)
245
+ pageMarginX: z.number().default(30),
246
+ pageMarginTop: z.number().default(30),
247
+ pageMarginBottom: z.number().default(30)
243
248
  });
244
249
  var Locale = z.object({
245
250
  language: z.string().default("en-GB"),
@@ -2894,10 +2899,38 @@ import path from "path";
2894
2899
  var ROOT = path.resolve(process.env.JUICED_DATA_DIR || path.join(process.cwd(), "../..", "data"));
2895
2900
  var RESUMES = path.join(ROOT, "resumes");
2896
2901
  var VERSIONS = path.join(ROOT, "versions");
2902
+ var CLOUD_TOKEN = (process.env.JUICEDRESUME_TOKEN || "").trim();
2903
+ var CLOUD_URL = (process.env.JUICEDRESUME_URL || "https://juicedresume.com").replace(/\/+$/, "");
2904
+ var CLOUD = CLOUD_TOKEN.length > 0;
2897
2905
  async function ensure(dir) {
2898
2906
  await fs.mkdir(dir, { recursive: true });
2899
2907
  }
2908
+ async function cloudReq(method, p, body) {
2909
+ let res;
2910
+ try {
2911
+ res = await fetch(`${CLOUD_URL}/api/mcp${p}`, {
2912
+ method,
2913
+ headers: {
2914
+ authorization: `Bearer ${CLOUD_TOKEN}`,
2915
+ ...body !== void 0 ? { "content-type": "application/json" } : {}
2916
+ },
2917
+ body: body !== void 0 ? JSON.stringify(body) : void 0
2918
+ });
2919
+ } catch (e) {
2920
+ throw new Error(`JuicedResume: could not reach ${CLOUD_URL} (${e?.message || e}). Check JUICEDRESUME_URL / network.`);
2921
+ }
2922
+ if (res.status === 401) throw new Error("JuicedResume: invalid or missing JUICEDRESUME_TOKEN \u2014 rotate it in Settings \u2192 AI on juicedresume.com.");
2923
+ if (res.status === 402) throw new Error("JuicedResume: your plan does not include MCP/cloud access (Basic or Pro required).");
2924
+ if (res.status === 404) return null;
2925
+ if (!res.ok) {
2926
+ const detail = await res.text().catch(() => "");
2927
+ throw new Error(`JuicedResume cloud ${method} ${p} failed: ${res.status} ${detail}`);
2928
+ }
2929
+ const ct = res.headers.get("content-type") || "";
2930
+ return ct.includes("application/json") ? res.json() : null;
2931
+ }
2900
2932
  async function listResumes() {
2933
+ if (CLOUD) return await cloudReq("GET", "/resumes") ?? [];
2901
2934
  await ensure(RESUMES);
2902
2935
  const files = await fs.readdir(RESUMES);
2903
2936
  const out = [];
@@ -2912,6 +2945,10 @@ async function listResumes() {
2912
2945
  return out.sort((a, b) => a.updatedAt < b.updatedAt ? 1 : -1);
2913
2946
  }
2914
2947
  async function getResume(id) {
2948
+ if (CLOUD) {
2949
+ const data = await cloudReq("GET", `/resumes/${id}`);
2950
+ return data ? Resume.parse(data) : null;
2951
+ }
2915
2952
  try {
2916
2953
  const raw = await fs.readFile(path.join(RESUMES, `${id}.json`), "utf8");
2917
2954
  return Resume.parse(JSON.parse(raw));
@@ -2920,9 +2957,13 @@ async function getResume(id) {
2920
2957
  }
2921
2958
  }
2922
2959
  async function saveResume(r) {
2960
+ const next = { ...r, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2961
+ if (CLOUD) {
2962
+ await cloudReq("PUT", `/resumes/${r.id}`, next);
2963
+ return next;
2964
+ }
2923
2965
  await ensure(RESUMES);
2924
2966
  await ensure(path.join(VERSIONS, r.id));
2925
- const next = { ...r, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2926
2967
  const file = path.join(RESUMES, `${r.id}.json`);
2927
2968
  try {
2928
2969
  const prev = await fs.readFile(file, "utf8");
@@ -2945,6 +2986,10 @@ async function createBlankResume(name = "Untitled") {
2945
2986
  return await saveResume(blank);
2946
2987
  }
2947
2988
  async function deleteResume(id) {
2989
+ if (CLOUD) {
2990
+ await cloudReq("DELETE", `/resumes/${id}`);
2991
+ return;
2992
+ }
2948
2993
  try {
2949
2994
  await fs.unlink(path.join(RESUMES, `${id}.json`));
2950
2995
  } catch {
@@ -3280,7 +3325,7 @@ function runOnce(cmd, args, cwd) {
3280
3325
  import { promises as fs3 } from "fs";
3281
3326
  import path3 from "path";
3282
3327
  var server = new Server(
3283
- { name: "juiced-resume", version: "0.2.0" },
3328
+ { name: "juiced-resume", version: "0.3.0" },
3284
3329
  { capabilities: { tools: {} } }
3285
3330
  );
3286
3331
  var idArg = z2.object({ id: z2.string() });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juicedresume/mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Model Context Protocol server for JuicedResume — score, tailor, and edit your resume from any MCP client (Claude Code, Claude Desktop, Cursor).",
5
5
  "keywords": [
6
6
  "mcp",
@@ -48,6 +48,7 @@
48
48
  "@juiced/scoring": "*",
49
49
  "@types/node": "^22.9.0",
50
50
  "esbuild": "^0.24.2",
51
+ "esbuild-wasm": "^0.24.2",
51
52
  "tsx": "^4.19.2",
52
53
  "typescript": "^5.6.3"
53
54
  },