@driftless-sh/cli 0.1.38 → 0.1.39

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.
package/dist/index.js CHANGED
@@ -39,8 +39,38 @@ __export(api_client_exports, {
39
39
  api: () => api,
40
40
  formatError: () => formatError,
41
41
  getApiKey: () => getApiKey,
42
- getApiUrl: () => getApiUrl
42
+ getApiUrl: () => getApiUrl,
43
+ getCachedWorkspace: () => getCachedWorkspace,
44
+ saveWorkspaceToConfig: () => saveWorkspaceToConfig
43
45
  });
46
+ function readConfig() {
47
+ try {
48
+ if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
49
+ return JSON.parse((0, import_node_fs.readFileSync)(CONFIG_PATH, "utf8"));
50
+ }
51
+ } catch {
52
+ }
53
+ return {};
54
+ }
55
+ function getCachedWorkspace() {
56
+ const c = readConfig();
57
+ return { slug: c.workspace_slug, workspaceId: c.workspace_id };
58
+ }
59
+ function saveWorkspaceToConfig(slug, workspaceId) {
60
+ if (!slug) return;
61
+ try {
62
+ const c = readConfig();
63
+ if (c.workspace_slug === slug && (workspaceId === void 0 || c.workspace_id === workspaceId)) {
64
+ return;
65
+ }
66
+ c.workspace_slug = slug;
67
+ if (workspaceId !== void 0) c.workspace_id = workspaceId;
68
+ const dir = (0, import_node_path.dirname)(CONFIG_PATH);
69
+ if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
70
+ (0, import_node_fs.writeFileSync)(CONFIG_PATH, JSON.stringify(c, null, 2) + "\n");
71
+ } catch {
72
+ }
73
+ }
44
74
  function loadApiKey() {
45
75
  const envKey = process.env["DRIFTLESS_API_KEY"];
46
76
  if (envKey) return envKey;
@@ -94,7 +124,7 @@ function parseError(e) {
94
124
  }
95
125
  return msg;
96
126
  }
97
- function request(method, path, body) {
127
+ function singleRequest(method, path, body, timeoutMs) {
98
128
  return new Promise((resolve8, reject) => {
99
129
  const baseUrl = getBaseUrl();
100
130
  const fullUrl = `${baseUrl}${path}`;
@@ -128,11 +158,33 @@ function request(method, path, body) {
128
158
  });
129
159
  }
130
160
  );
161
+ if (timeoutMs && timeoutMs > 0) {
162
+ req.setTimeout(timeoutMs, () => {
163
+ req.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
164
+ });
165
+ }
131
166
  req.on("error", (e) => reject(new Error(`Connection failed: ${e.message}`)));
132
167
  if (body) req.write(JSON.stringify(body));
133
168
  req.end();
134
169
  });
135
170
  }
171
+ function isTransient(err) {
172
+ const msg = err instanceof Error ? err.message : String(err);
173
+ return !/HTTP 4\d\d:/.test(msg);
174
+ }
175
+ async function request(method, path, body, opts) {
176
+ const retries = opts?.retries ?? 0;
177
+ let lastErr;
178
+ for (let attempt = 0; attempt <= retries; attempt++) {
179
+ try {
180
+ return await singleRequest(method, path, body, opts?.timeoutMs);
181
+ } catch (e) {
182
+ lastErr = e;
183
+ if (attempt === retries || !isTransient(e)) break;
184
+ }
185
+ }
186
+ throw lastErr;
187
+ }
136
188
  function getApiUrl() {
137
189
  return getBaseUrl();
138
190
  }
@@ -154,11 +206,11 @@ var init_api_client = __esm({
154
206
  CONFIG_PATH = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".driftless", "config.json");
155
207
  DEFAULT_URL = "http://localhost:3000/api/v1";
156
208
  api = {
157
- get: (path) => request("GET", path),
158
- post: (path, body) => request("POST", path, body),
159
- put: (path, body) => request("PUT", path, body),
160
- patch: (path, body) => request("PATCH", path, body),
161
- delete: (path) => request("DELETE", path)
209
+ get: (path, opts) => request("GET", path, void 0, opts),
210
+ post: (path, body, opts) => request("POST", path, body, opts),
211
+ put: (path, body, opts) => request("PUT", path, body, opts),
212
+ patch: (path, body, opts) => request("PATCH", path, body, opts),
213
+ delete: (path, opts) => request("DELETE", path, void 0, opts)
162
214
  };
163
215
  }
164
216
  });
@@ -3184,7 +3236,7 @@ var require_typescript = __commonJS({
3184
3236
  forEachYieldExpression: () => forEachYieldExpression,
3185
3237
  formatColorAndReset: () => formatColorAndReset,
3186
3238
  formatDiagnostic: () => formatDiagnostic,
3187
- formatDiagnostics: () => formatDiagnostics,
3239
+ formatDiagnostics: () => formatDiagnostics2,
3188
3240
  formatDiagnosticsWithColorAndContext: () => formatDiagnosticsWithColorAndContext,
3189
3241
  formatGeneratedName: () => formatGeneratedName,
3190
3242
  formatGeneratedNamePart: () => formatGeneratedNamePart,
@@ -138049,7 +138101,7 @@ ${lanes.join("\n")}
138049
138101
  }
138050
138102
  return sortAndDeduplicateDiagnostics(diagnostics || emptyArray);
138051
138103
  }
138052
- function formatDiagnostics(diagnostics, host) {
138104
+ function formatDiagnostics2(diagnostics, host) {
138053
138105
  let output = "";
138054
138106
  for (const diagnostic of diagnostics) {
138055
138107
  output += formatDiagnostic(diagnostic, host);
@@ -198687,7 +198739,7 @@ ${options.prefix}` : "\n" : options.prefix
198687
198739
  forEachYieldExpression: () => forEachYieldExpression,
198688
198740
  formatColorAndReset: () => formatColorAndReset,
198689
198741
  formatDiagnostic: () => formatDiagnostic,
198690
- formatDiagnostics: () => formatDiagnostics,
198742
+ formatDiagnostics: () => formatDiagnostics2,
198691
198743
  formatDiagnosticsWithColorAndContext: () => formatDiagnosticsWithColorAndContext,
198692
198744
  formatGeneratedName: () => formatGeneratedName,
198693
198745
  formatGeneratedNamePart: () => formatGeneratedNamePart,
@@ -214581,7 +214633,7 @@ async function installSkillCommand() {
214581
214633
  // src/commands/init.ts
214582
214634
  function getVersion() {
214583
214635
  try {
214584
- return "0.1.38";
214636
+ return "0.1.39";
214585
214637
  } catch {
214586
214638
  return "0.0.0";
214587
214639
  }
@@ -214922,6 +214974,7 @@ async function initCommand(args) {
214922
214974
  process.exit(1);
214923
214975
  }
214924
214976
  const workspaceSlug = workspace.slug;
214977
+ if (workspaceSlug) saveWorkspaceToConfig(workspaceSlug, workspace.workspace_id);
214925
214978
  console.log(`Repository: ${remote.org}/${remote.repo}`);
214926
214979
  console.log(`Workspace: ${workspaceSlug} \u2713`);
214927
214980
  if (srcOverride) console.log(`Scan root: ${srcOverride}`);
@@ -215103,16 +215156,27 @@ async function scanCommand(args) {
215103
215156
  process.exit(1);
215104
215157
  }
215105
215158
  const isJSON = args.includes("--json");
215106
- let workspace;
215159
+ let workspaceSlug;
215160
+ let workspaceId;
215107
215161
  try {
215108
- workspace = await api.get("/me");
215162
+ const me = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
215163
+ workspaceSlug = me?.slug;
215164
+ workspaceId = me?.workspace_id;
215165
+ if (workspaceSlug) saveWorkspaceToConfig(workspaceSlug, workspaceId);
215109
215166
  } catch {
215167
+ }
215168
+ if (!workspaceSlug) {
215169
+ const cached = getCachedWorkspace();
215170
+ workspaceSlug = cached.slug;
215171
+ workspaceId = workspaceId ?? cached.workspaceId;
215172
+ }
215173
+ if (!workspaceSlug) {
215110
215174
  console.error("Could not resolve workspace. Run driftless login first.");
215111
215175
  process.exit(1);
215112
215176
  }
215113
- const repos = await api.get(`/workspaces/${workspace.slug}/repos`);
215177
+ const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
215114
215178
  if (!Array.isArray(repos)) {
215115
- console.error(`Error: Could not fetch repos for workspace '${workspace.slug}'.`);
215179
+ console.error(`Error: Could not fetch repos for workspace '${workspaceSlug}'.`);
215116
215180
  console.error(`Response:`, JSON.stringify(repos).slice(0, 200));
215117
215181
  console.error("\nTip: Make sure your API key belongs to this workspace.");
215118
215182
  console.error("Run: driftless login --key <key>");
@@ -215156,7 +215220,7 @@ async function scanCommand(args) {
215156
215220
  }
215157
215221
  }
215158
215222
  const result = await api.post("/scan", {
215159
- workspace_id: workspace.workspace_id,
215223
+ workspace_id: workspaceId,
215160
215224
  repo_id: repo.id,
215161
215225
  diff,
215162
215226
  commit_hash: commitHash,
@@ -215164,7 +215228,7 @@ async function scanCommand(args) {
215164
215228
  });
215165
215229
  const rulesEvaluated = result.rules_evaluated || 0;
215166
215230
  if (result.status === "clean") {
215167
- analyticsEvent("cli_scan_run", workspace.slug, { violations_found: false, count: 0 });
215231
+ analyticsEvent("cli_scan_run", workspaceSlug, { violations_found: false, count: 0 });
215168
215232
  if (isJSON) {
215169
215233
  emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
215170
215234
  } else {
@@ -215173,7 +215237,7 @@ async function scanCommand(args) {
215173
215237
  process.exit(0);
215174
215238
  }
215175
215239
  if (!result.violations || result.violations.length === 0) {
215176
- analyticsEvent("cli_scan_run", workspace.slug, { violations_found: false, count: 0 });
215240
+ analyticsEvent("cli_scan_run", workspaceSlug, { violations_found: false, count: 0 });
215177
215241
  if (isJSON) {
215178
215242
  emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
215179
215243
  } else {
@@ -215181,7 +215245,7 @@ async function scanCommand(args) {
215181
215245
  }
215182
215246
  process.exit(0);
215183
215247
  }
215184
- analyticsEvent("cli_scan_run", workspace.slug, { violations_found: true, count: result.violations.length });
215248
+ analyticsEvent("cli_scan_run", workspaceSlug, { violations_found: true, count: result.violations.length });
215185
215249
  if (isJSON) {
215186
215250
  emitJSON({ status: "violated", files: changedFiles, rules_evaluated: rulesEvaluated, violations: result.violations });
215187
215251
  } else {
@@ -215379,10 +215443,15 @@ function countLocalFilesMatching(pattern) {
215379
215443
  }
215380
215444
  async function resolveWorkspaceSlug() {
215381
215445
  try {
215382
- const me = await api.get("/me");
215383
- if (me?.slug) return me.slug;
215446
+ const me = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
215447
+ if (me?.slug) {
215448
+ saveWorkspaceToConfig(me.slug, me.workspace_id);
215449
+ return me.slug;
215450
+ }
215384
215451
  } catch {
215385
215452
  }
215453
+ const cached = getCachedWorkspace().slug;
215454
+ if (cached) return cached;
215386
215455
  const remote = getGitRemote();
215387
215456
  if (!remote) {
215388
215457
  console.error("Error: no git remote found.");
@@ -216045,30 +216114,59 @@ init_api_client();
216045
216114
  function notLinkedMessage(remote, workspaceSlug) {
216046
216115
  return `Repo '${remote.org}/${remote.repo}' is not registered in workspace '${workspaceSlug}'. Run \`driftless init\` to register it.`;
216047
216116
  }
216117
+ function formatDiagnostics(d) {
216118
+ const lines = [];
216119
+ if (d.remote) lines.push(` repo: ${d.remote.org}/${d.remote.repo}`);
216120
+ lines.push(` tried slugs: ${d.triedSlugs.length ? d.triedSlugs.join(", ") : "(none)"}`);
216121
+ lines.push(` config slug: ${d.configSlug ?? "(not cached \u2014 run `driftless login` or `driftless init`)"}`);
216122
+ if (!d.meAttempted) {
216123
+ lines.push(" /me: not attempted");
216124
+ } else if (d.meReturned) {
216125
+ lines.push(` /me: returned slug=${d.meSlug ?? "(none)"} workspace_id=${d.meWorkspaceId ?? "(none)"}`);
216126
+ } else {
216127
+ lines.push(` /me: FAILED \u2014 ${d.meError ?? "unknown error"}`);
216128
+ }
216129
+ if (d.failedEndpoint) lines.push(` last endpoint: ${d.failedEndpoint} (rejected)`);
216130
+ return lines.join("\n");
216131
+ }
216048
216132
  async function fetchRepos(slug) {
216049
216133
  try {
216050
216134
  const raw = await api.get(`/workspaces/${slug}/repos`);
216051
- return Array.isArray(raw) ? raw : null;
216052
- } catch {
216053
- return null;
216135
+ if (Array.isArray(raw)) return { repos: raw };
216136
+ return { error: "unexpected response shape" };
216137
+ } catch (e) {
216138
+ return { error: e instanceof Error ? e.message : String(e) };
216054
216139
  }
216055
216140
  }
216056
216141
  async function resolveRepo() {
216057
216142
  const remote = getGitRemote();
216058
216143
  if (!remote) return { ok: false, reason: "no_remote" };
216144
+ const configSlug = getCachedWorkspace().slug;
216059
216145
  let meSlug;
216146
+ let meWorkspaceId;
216147
+ let meReturned = false;
216148
+ let meError;
216060
216149
  try {
216061
- const me = await api.get("/me");
216150
+ const me = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
216151
+ meReturned = true;
216062
216152
  meSlug = me?.slug;
216063
- } catch {
216153
+ meWorkspaceId = me?.workspace_id;
216154
+ if (meSlug) saveWorkspaceToConfig(meSlug, meWorkspaceId);
216155
+ } catch (e) {
216156
+ meError = e instanceof Error ? e.message : String(e);
216064
216157
  }
216065
- const candidates = [...new Set([meSlug, remote.org].filter(Boolean))];
216158
+ const candidates = [...new Set([configSlug, meSlug, remote.org].filter(Boolean))];
216066
216159
  let lastLinkedSlug;
216160
+ let failedEndpoint;
216067
216161
  for (const slug of candidates) {
216068
- const repos = await fetchRepos(slug);
216069
- if (!repos) continue;
216070
- const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216162
+ const result = await fetchRepos(slug);
216163
+ if ("error" in result) {
216164
+ failedEndpoint = `/workspaces/${slug}/repos`;
216165
+ continue;
216166
+ }
216167
+ const repo = result.repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216071
216168
  if (repo) {
216169
+ saveWorkspaceToConfig(slug, meWorkspaceId);
216072
216170
  return {
216073
216171
  ok: true,
216074
216172
  workspaceSlug: slug,
@@ -216079,11 +216177,27 @@ async function resolveRepo() {
216079
216177
  }
216080
216178
  lastLinkedSlug = slug;
216081
216179
  }
216180
+ const diagnostics = {
216181
+ remote,
216182
+ triedSlugs: candidates,
216183
+ configSlug,
216184
+ meAttempted: true,
216185
+ meReturned,
216186
+ meSlug,
216187
+ meWorkspaceId,
216188
+ meError,
216189
+ failedEndpoint
216190
+ };
216082
216191
  if (lastLinkedSlug) {
216083
- return { ok: false, reason: "not_linked", workspaceSlug: lastLinkedSlug, remote };
216192
+ return { ok: false, reason: "not_linked", workspaceSlug: lastLinkedSlug, remote, diagnostics };
216084
216193
  }
216085
- const triedSlugs = candidates.join(", ");
216086
- return { ok: false, reason: "no_workspace", remote, detail: `tried slugs: ${triedSlugs}` };
216194
+ return {
216195
+ ok: false,
216196
+ reason: "no_workspace",
216197
+ remote,
216198
+ detail: formatDiagnostics(diagnostics),
216199
+ diagnostics
216200
+ };
216087
216201
  }
216088
216202
 
216089
216203
  // src/commands/sync.ts
@@ -216113,14 +216227,31 @@ async function syncCommand(args) {
216113
216227
  const isJSON = !!flags["json"];
216114
216228
  const resolution = await resolveRepo();
216115
216229
  if (!resolution.ok) {
216116
- const base = resolution.reason === "no_remote" ? "Error: no git remote configured." : resolution.reason === "no_workspace" ? "Error: could not resolve workspace." : notLinkedMessage(resolution.remote, resolution.workspaceSlug);
216117
- const detail = resolution.reason === "no_workspace" && resolution.detail ? ` (${resolution.detail})` : "";
216118
- const msg = base + detail;
216230
+ if (resolution.reason === "no_remote") {
216231
+ if (!isJSON) console.log("\u26A0 Error: no git remote configured.");
216232
+ else emitJSON3({ error: "no_remote", message: "no git remote configured" });
216233
+ process.exit(1);
216234
+ }
216235
+ if (resolution.reason === "not_linked") {
216236
+ const msg = notLinkedMessage(resolution.remote, resolution.workspaceSlug);
216237
+ if (!isJSON) {
216238
+ console.log(`\u26A0 ${msg}`);
216239
+ console.log(" Run `driftless doctor` to diagnose.");
216240
+ } else {
216241
+ emitJSON3({ error: "not_linked", message: msg, diagnostics: resolution.diagnostics });
216242
+ }
216243
+ process.exit(1);
216244
+ }
216119
216245
  if (!isJSON) {
216120
- console.log(`\u26A0 ${msg}`);
216121
- if (resolution.reason !== "no_remote") console.log(" Run `driftless doctor` to diagnose.");
216246
+ console.log("\u26A0 Error: could not resolve workspace.");
216247
+ if (resolution.detail) console.log(resolution.detail);
216248
+ console.log(" Run `driftless doctor` to diagnose.");
216122
216249
  } else {
216123
- emitJSON3({ error: resolution.reason, message: msg });
216250
+ emitJSON3({
216251
+ error: "no_workspace",
216252
+ message: "could not resolve workspace",
216253
+ diagnostics: resolution.diagnostics
216254
+ });
216124
216255
  }
216125
216256
  process.exit(1);
216126
216257
  }
@@ -216203,6 +216334,7 @@ var import_node_path6 = require("node:path");
216203
216334
  var import_node_readline = require("node:readline");
216204
216335
  var import_node_child_process2 = require("node:child_process");
216205
216336
  var import_node_os2 = require("node:os");
216337
+ init_api_client();
216206
216338
  var CONFIG_DIR = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".driftless");
216207
216339
  var CONFIG_PATH2 = (0, import_node_path6.resolve)(CONFIG_DIR, "config.json");
216208
216340
  function openBrowser(url) {
@@ -216214,7 +216346,7 @@ async function loginCommand(args) {
216214
216346
  const keyIndex = args.indexOf("--key");
216215
216347
  if (keyIndex !== -1 && args[keyIndex + 1]) {
216216
216348
  const apiKey2 = args[keyIndex + 1];
216217
- saveConfig(apiKey2);
216349
+ await saveConfig(apiKey2);
216218
216350
  return;
216219
216351
  }
216220
216352
  const apiUrl = process.env["DRIFTLESS_API_URL"] || "https://api.driftless.icu/api/v1";
@@ -216245,9 +216377,9 @@ async function loginCommand(args) {
216245
216377
  console.error("Get a valid key from your Driftless Dashboard \u2192 Settings \u2192 API Keys.");
216246
216378
  process.exit(1);
216247
216379
  }
216248
- saveConfig(apiKey, apiUrl);
216380
+ await saveConfig(apiKey, apiUrl);
216249
216381
  }
216250
- function saveConfig(apiKey, apiUrl) {
216382
+ async function saveConfig(apiKey, apiUrl) {
216251
216383
  const url = apiUrl || "https://api.driftless.icu/api/v1";
216252
216384
  try {
216253
216385
  if (!(0, import_node_fs6.existsSync)(CONFIG_DIR)) {
@@ -216257,16 +216389,24 @@ function saveConfig(apiKey, apiUrl) {
216257
216389
  CONFIG_PATH2,
216258
216390
  JSON.stringify({ api_key: apiKey, api_url: url }, null, 2) + "\n"
216259
216391
  );
216260
- console.log();
216261
- console.log("Logged in successfully.");
216262
- console.log(` Config: ${CONFIG_PATH2}`);
216263
- console.log();
216264
- console.log("Try: driftless scan --diff");
216265
- process.exit(0);
216266
216392
  } catch (err) {
216267
216393
  console.error("Failed to save config:", err);
216268
216394
  process.exit(1);
216269
216395
  }
216396
+ console.log();
216397
+ console.log("Logged in successfully.");
216398
+ console.log(` Config: ${CONFIG_PATH2}`);
216399
+ try {
216400
+ const me = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
216401
+ if (me?.slug) {
216402
+ saveWorkspaceToConfig(me.slug, me.workspace_id);
216403
+ console.log(` Workspace: ${me.slug}`);
216404
+ }
216405
+ } catch {
216406
+ }
216407
+ console.log();
216408
+ console.log("Try: driftless scan --diff");
216409
+ process.exit(0);
216270
216410
  }
216271
216411
 
216272
216412
  // src/commands/doctor.ts
@@ -216298,33 +216438,27 @@ async function doctorCommand() {
216298
216438
  async function getMe() {
216299
216439
  if (meCache) return meCache;
216300
216440
  try {
216301
- meCache = await api.get("/me");
216441
+ meCache = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
216302
216442
  } catch {
216303
216443
  meCache = null;
216304
216444
  }
216305
216445
  return meCache;
216306
216446
  }
216307
- let reposCache = null;
216308
- async function getRepos(slug) {
216309
- if (reposCache) return reposCache;
216310
- try {
216311
- reposCache = await api.get(`/workspaces/${slug}/repos`);
216312
- } catch {
216313
- reposCache = null;
216314
- }
216315
- return reposCache;
216316
- }
216317
- if (apiKey && apiUrl !== "http://localhost:3000/api/v1") {
216318
- const me = await getMe();
216319
- if (me?.slug) {
216320
- checks.push({ name: "Workspace", status: "ok", detail: me.slug });
216321
- } else {
216322
- checks.push({ name: "Workspace", status: "warn", detail: "API returned no workspace. Run `driftless init`" });
216323
- }
216324
- } else {
216447
+ const resolution = await resolveRepo();
216448
+ const localOrNoKey = !apiKey || apiUrl === "http://localhost:3000/api/v1";
216449
+ if (localOrNoKey) {
216325
216450
  checks.push({ name: "Workspace", status: "warn", detail: "Skipped (local API or no key)" });
216451
+ } else if (resolution.ok) {
216452
+ checks.push({ name: "Workspace", status: "ok", detail: resolution.workspaceSlug });
216453
+ } else if (resolution.reason === "not_linked" && resolution.workspaceSlug) {
216454
+ checks.push({ name: "Workspace", status: "ok", detail: resolution.workspaceSlug });
216455
+ } else if (resolution.reason === "no_workspace" && resolution.diagnostics) {
216456
+ const d = resolution.diagnostics;
216457
+ const detail = !d.meReturned && d.meError ? `/me failed (${d.meError}); cached=${d.configSlug ?? "none"}` : "Could not resolve. Run `driftless init`";
216458
+ checks.push({ name: "Workspace", status: "warn", detail });
216459
+ } else {
216460
+ checks.push({ name: "Workspace", status: "warn", detail: "Could not resolve (no git remote)" });
216326
216461
  }
216327
- const resolution = await resolveRepo();
216328
216462
  if (resolution.ok) {
216329
216463
  checks.push({ name: "Repo linked", status: "ok", detail: `${resolution.remote.org}/${resolution.remote.repo}` });
216330
216464
  checks.push({
@@ -216393,6 +216527,10 @@ async function doctorCommand() {
216393
216527
  }
216394
216528
  console.log(`
216395
216529
  ${okCount} ok, ${warnCount} warnings, ${failCount} failures`);
216530
+ if (!resolution.ok && resolution.reason === "no_workspace" && resolution.diagnostics) {
216531
+ console.log("\nWorkspace resolution diagnostics:");
216532
+ console.log(formatDiagnostics(resolution.diagnostics));
216533
+ }
216396
216534
  if (failCount > 0) {
216397
216535
  console.log("\nFix failures before running `driftless init` or `driftless scan`.");
216398
216536
  process.exit(1);
@@ -216405,7 +216543,7 @@ function pad2(s, n) {
216405
216543
  }
216406
216544
 
216407
216545
  // src/index.ts
216408
- var VERSION = "0.1.38";
216546
+ var VERSION = "0.1.39";
216409
216547
  var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
216410
216548
 
216411
216549
  Install: npm install -g @driftless-sh/cli