@iann29/synapse 1.8.4 → 1.8.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.
@@ -222,8 +222,11 @@ show up in the menu).`,
222
222
  if (prod) {
223
223
  ctx.out.info(`Selected prod deployment ${colors.bold(prod.name)}.`);
224
224
  } else {
225
+ // v1.8.5: surface the dashboard URL so the operator can click
226
+ // straight to "New deployment" instead of hunting for it.
227
+ const projectUrl = `${cfg.baseUrl}/teams/${encodeURIComponent(team.slug || team.id)}/${encodeURIComponent(project.id)}`;
225
228
  ctx.out.warn(
226
- "no prod deployment found. `synapse deploy` (and `synapse convex deploy`) will fail with a clear error until you create a prod deployment and run `synapse select` again.",
229
+ `no prod deployment found. Create one at ${projectUrl} "New deployment" mark as PROD, then run \`synapse select\` again. \`synapse deploy\` fails until then.`,
227
230
  );
228
231
  }
229
232
  if (process.env.CONVEX_DEPLOYMENT) {
@@ -234,6 +237,9 @@ show up in the menu).`,
234
237
  ctx.out.info(
235
238
  `\nNext step: run ${colors.bold("synapse dev")} (or ${colors.bold("npx convex dev")}) once in this directory to push your schema and watch for changes.`,
236
239
  );
240
+ ctx.out.info(
241
+ `Or run ${colors.bold("synapse doctor")} first to confirm everything is healthy (add ${colors.bold("--fix")} to auto-clean .gitignore + file modes).`,
242
+ );
237
243
  },
238
244
  );
239
245
  // Force ctx.projectConfig to re-read on next access (commands chained
@@ -146,12 +146,27 @@ Run \`synapse doctor\` for a deeper health check.`,
146
146
  ],
147
147
  );
148
148
  const broken = rows.filter((r) => r.urlForm === "host").length;
149
+ const hasProd = rows.some((r) => r.type === "prod");
150
+ const hasDev = rows.some((r) => r.type === "dev");
151
+
149
152
  if (broken > 0) {
150
153
  stdout.write("\n");
151
154
  ctx.out.warn(
152
- `${broken} deployment${broken > 1 ? "s" : ""} not browser-reachable — set SYNAPSE_BASE_DOMAIN on the server OR add a custom domain per deployment. Run \`synapse doctor\` for the full diagnosis.`,
155
+ `${broken} deployment${broken > 1 ? "s" : ""} not browser-reachable — instance admin can enable wildcard at ${baseUrl}/admin/host-domain, or add a custom domain per deployment.`,
153
156
  );
154
157
  }
158
+ // v1.8.5 hints: when nothing's broken, surface the next-step
159
+ // bread-crumb so first-time operators know what to do next.
160
+ if (broken === 0 && rows.length > 0) {
161
+ stdout.write("\n");
162
+ if (!hasProd && hasDev) {
163
+ ctx.out.info(
164
+ `No prod deployment yet. Create one at ${baseUrl}/teams/<team>/${projectId} → "New deployment" → PROD, then \`synapse select\` again.`,
165
+ );
166
+ } else if (hasDev) {
167
+ ctx.out.info("Run `synapse dev` to start a Convex dev session in this directory.");
168
+ }
169
+ }
155
170
  },
156
171
  );
157
172
  },
@@ -33,6 +33,18 @@ function renderHeader(report, write) {
33
33
  write(parts.join(" ") + "\n\n");
34
34
  }
35
35
 
36
+ // v1.8.5: inline-tag formatter for the fix hint. Mirrors the cyan-on-dim
37
+ // rest of the remediation line; falls back to plain text if colors.cyan
38
+ // isn't defined (the `colors` shim is a no-op on non-tty stdouts).
39
+ function fixableTag(r) {
40
+ if (!r.fixable) return "";
41
+ if (r.fixedBy) return ""; // already fixed; no nudge needed
42
+ const text = r.fixable === "auto"
43
+ ? " [run `synapse doctor --fix`]"
44
+ : " [run `synapse doctor --fix --yes`]";
45
+ return colors.cyan ? colors.cyan(text) : text;
46
+ }
47
+
36
48
  function renderCheck(r, { verbose }, write) {
37
49
  const sym = TONE[r.status](SYMBOL[r.status]);
38
50
  write(` ${sym} ${r.title}`);
@@ -40,7 +52,9 @@ function renderCheck(r, { verbose }, write) {
40
52
  if (r.fixedBy) write(` ${colors.cyan ? colors.cyan("↻ " + r.fixedBy) : "↻ " + r.fixedBy}`);
41
53
  write("\n");
42
54
  if (r.remediation && (r.status === "issue" || r.status === "warn")) {
43
- write(colors.dim(` → ${r.remediation}\n`));
55
+ write(colors.dim(` → ${r.remediation}`));
56
+ write(fixableTag(r));
57
+ write("\n");
44
58
  }
45
59
  if (verbose && r.detail) {
46
60
  write(colors.dim(` ${r.detail.replace(/\n/g, "\n ")}\n`));
@@ -88,6 +102,39 @@ function renderReport(report, { stdout, verbose = false } = {}) {
88
102
  if (report.exitCode !== 0) {
89
103
  write(` ${colors.dim("exit " + report.exitCode)}\n`);
90
104
  }
105
+
106
+ // v1.8.5: actionable tip footer. Surfaces the right `--fix` invocation
107
+ // for whichever class of fixable issues remains, so an operator who
108
+ // just sees a wall of `! ...` warnings knows exactly which command
109
+ // would clean them up — no need to memorise the auto-vs-prompt split.
110
+ const autoCount = t.fixableAuto || 0;
111
+ const promptCount = t.fixablePrompt || 0;
112
+ const tip = (msg) =>
113
+ ` ${colors.cyan ? colors.cyan("💡 Tip:") : "Tip:"} ${msg}\n`;
114
+ if (autoCount + promptCount > 0) {
115
+ write("\n");
116
+ if (autoCount > 0 && promptCount > 0) {
117
+ write(tip(
118
+ `${autoCount} auto-fixable + ${promptCount} prompt-fixable — run \`synapse doctor --fix --yes\` to apply both.`,
119
+ ));
120
+ } else if (autoCount > 0) {
121
+ write(tip(
122
+ `${autoCount} ${autoCount === 1 ? "issue is" : "issues are"} auto-fixable — run \`synapse doctor --fix\`.`,
123
+ ));
124
+ } else {
125
+ write(tip(
126
+ `${promptCount} ${promptCount === 1 ? "issue needs" : "issues need"} confirmation — run \`synapse doctor --fix --yes\`.`,
127
+ ));
128
+ }
129
+ } else if (t.warn + t.issue === 0 && t.ok > 0) {
130
+ // Clean bill of health → point at the next step. Operators who run
131
+ // doctor first thing after `synapse select` shouldn't have to wonder
132
+ // what's next.
133
+ write("\n");
134
+ write(tip(
135
+ "everything healthy — run `synapse dev` to start working, or `synapse status` to list deployments.",
136
+ ));
137
+ }
91
138
  }
92
139
 
93
140
  module.exports = { renderReport };
@@ -53,10 +53,21 @@ async function runChecks(checks, ctx) {
53
53
  continue;
54
54
  }
55
55
  const result = await check.run(ctx);
56
+ // `fixable` carries the autoFix kind into the result so the
57
+ // renderer (and --json consumers) can surface a "this is
58
+ // fixable, here's the right flag" hint without re-importing
59
+ // the check catalog. Skipped checks aren't marked fixable —
60
+ // the fix wouldn't have ctx to operate on anyway.
61
+ const fixable =
62
+ typeof check.fix === "function" &&
63
+ (check.autoFix === "auto" || check.autoFix === "prompt")
64
+ ? check.autoFix
65
+ : null;
56
66
  resultsById.set(id, {
57
67
  id,
58
68
  category: check.category,
59
69
  title: check.title,
70
+ fixable,
60
71
  ...result,
61
72
  });
62
73
  pending.delete(id);
@@ -83,13 +94,28 @@ async function runChecks(checks, ctx) {
83
94
  }
84
95
 
85
96
  function totalize(results) {
86
- const t = { ok: 0, warn: 0, issue: 0, skipped: 0, fixed: 0 };
97
+ const t = {
98
+ ok: 0,
99
+ warn: 0,
100
+ issue: 0,
101
+ skipped: 0,
102
+ fixed: 0,
103
+ // v1.8.5: count of results still in warn/issue that have a
104
+ // registered fix. Splits auto vs prompt so the renderer can show
105
+ // the right CLI hint at the bottom.
106
+ fixableAuto: 0,
107
+ fixablePrompt: 0,
108
+ };
87
109
  for (const r of results) {
88
110
  if (r.fixedBy) t.fixed += 1;
89
111
  if (r.status === "ok") t.ok += 1;
90
112
  else if (r.status === "warn") t.warn += 1;
91
113
  else if (r.status === "issue") t.issue += 1;
92
114
  else if (r.status === "skipped") t.skipped += 1;
115
+ if ((r.status === "warn" || r.status === "issue") && !r.fixedBy) {
116
+ if (r.fixable === "auto") t.fixableAuto += 1;
117
+ else if (r.fixable === "prompt") t.fixablePrompt += 1;
118
+ }
93
119
  }
94
120
  return t;
95
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iann29/synapse",
3
- "version": "1.8.4",
3
+ "version": "1.8.5",
4
4
  "description": "Thin CLI wrapper for using the official Convex CLI with Synapse-managed deployments.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {