@driftless-sh/cli 0.1.37 → 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.37";
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,89 @@ 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
+ }
216132
+ async function fetchRepos(slug) {
216133
+ try {
216134
+ const raw = await api.get(`/workspaces/${slug}/repos`);
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) };
216139
+ }
216140
+ }
216048
216141
  async function resolveRepo() {
216049
216142
  const remote = getGitRemote();
216050
216143
  if (!remote) return { ok: false, reason: "no_remote" };
216051
- let workspaceSlug;
216144
+ const configSlug = getCachedWorkspace().slug;
216145
+ let meSlug;
216146
+ let meWorkspaceId;
216147
+ let meReturned = false;
216148
+ let meError;
216052
216149
  try {
216053
- const me = await api.get("/me");
216054
- workspaceSlug = me?.slug;
216055
- } catch {
216150
+ const me = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
216151
+ meReturned = true;
216152
+ meSlug = me?.slug;
216153
+ meWorkspaceId = me?.workspace_id;
216154
+ if (meSlug) saveWorkspaceToConfig(meSlug, meWorkspaceId);
216155
+ } catch (e) {
216156
+ meError = e instanceof Error ? e.message : String(e);
216056
216157
  }
216057
- const slug = workspaceSlug ?? remote.org;
216058
- let repos = null;
216059
- try {
216060
- repos = await api.get(`/workspaces/${slug}/repos`);
216061
- } catch {
216158
+ const candidates = [...new Set([configSlug, meSlug, remote.org].filter(Boolean))];
216159
+ let lastLinkedSlug;
216160
+ let failedEndpoint;
216161
+ for (const slug of candidates) {
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);
216168
+ if (repo) {
216169
+ saveWorkspaceToConfig(slug, meWorkspaceId);
216170
+ return {
216171
+ ok: true,
216172
+ workspaceSlug: slug,
216173
+ repoId: repo.id,
216174
+ remote,
216175
+ hasScanBaseline: !!repo.scan_summary
216176
+ };
216177
+ }
216178
+ lastLinkedSlug = slug;
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
+ };
216191
+ if (lastLinkedSlug) {
216192
+ return { ok: false, reason: "not_linked", workspaceSlug: lastLinkedSlug, remote, diagnostics };
216062
216193
  }
216063
- if (!repos) return { ok: false, reason: "no_workspace", remote };
216064
- const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216065
- if (!repo) return { ok: false, reason: "not_linked", workspaceSlug: slug, remote };
216066
216194
  return {
216067
- ok: true,
216068
- workspaceSlug: slug,
216069
- repoId: repo.id,
216195
+ ok: false,
216196
+ reason: "no_workspace",
216070
216197
  remote,
216071
- hasScanBaseline: !!repo.scan_summary
216198
+ detail: formatDiagnostics(diagnostics),
216199
+ diagnostics
216072
216200
  };
216073
216201
  }
216074
216202
 
@@ -216099,12 +216227,31 @@ async function syncCommand(args) {
216099
216227
  const isJSON = !!flags["json"];
216100
216228
  const resolution = await resolveRepo();
216101
216229
  if (!resolution.ok) {
216102
- const msg = resolution.reason === "no_remote" ? "Error: no git remote configured." : resolution.reason === "no_workspace" ? "Error: could not resolve workspace. Run `driftless doctor` to diagnose." : notLinkedMessage(resolution.remote, resolution.workspaceSlug);
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
+ }
216103
216245
  if (!isJSON) {
216104
- console.log(`\u26A0 ${msg}`);
216105
- 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.");
216106
216249
  } else {
216107
- emitJSON3({ error: resolution.reason, message: msg });
216250
+ emitJSON3({
216251
+ error: "no_workspace",
216252
+ message: "could not resolve workspace",
216253
+ diagnostics: resolution.diagnostics
216254
+ });
216108
216255
  }
216109
216256
  process.exit(1);
216110
216257
  }
@@ -216187,6 +216334,7 @@ var import_node_path6 = require("node:path");
216187
216334
  var import_node_readline = require("node:readline");
216188
216335
  var import_node_child_process2 = require("node:child_process");
216189
216336
  var import_node_os2 = require("node:os");
216337
+ init_api_client();
216190
216338
  var CONFIG_DIR = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".driftless");
216191
216339
  var CONFIG_PATH2 = (0, import_node_path6.resolve)(CONFIG_DIR, "config.json");
216192
216340
  function openBrowser(url) {
@@ -216198,7 +216346,7 @@ async function loginCommand(args) {
216198
216346
  const keyIndex = args.indexOf("--key");
216199
216347
  if (keyIndex !== -1 && args[keyIndex + 1]) {
216200
216348
  const apiKey2 = args[keyIndex + 1];
216201
- saveConfig(apiKey2);
216349
+ await saveConfig(apiKey2);
216202
216350
  return;
216203
216351
  }
216204
216352
  const apiUrl = process.env["DRIFTLESS_API_URL"] || "https://api.driftless.icu/api/v1";
@@ -216229,9 +216377,9 @@ async function loginCommand(args) {
216229
216377
  console.error("Get a valid key from your Driftless Dashboard \u2192 Settings \u2192 API Keys.");
216230
216378
  process.exit(1);
216231
216379
  }
216232
- saveConfig(apiKey, apiUrl);
216380
+ await saveConfig(apiKey, apiUrl);
216233
216381
  }
216234
- function saveConfig(apiKey, apiUrl) {
216382
+ async function saveConfig(apiKey, apiUrl) {
216235
216383
  const url = apiUrl || "https://api.driftless.icu/api/v1";
216236
216384
  try {
216237
216385
  if (!(0, import_node_fs6.existsSync)(CONFIG_DIR)) {
@@ -216241,16 +216389,24 @@ function saveConfig(apiKey, apiUrl) {
216241
216389
  CONFIG_PATH2,
216242
216390
  JSON.stringify({ api_key: apiKey, api_url: url }, null, 2) + "\n"
216243
216391
  );
216244
- console.log();
216245
- console.log("Logged in successfully.");
216246
- console.log(` Config: ${CONFIG_PATH2}`);
216247
- console.log();
216248
- console.log("Try: driftless scan --diff");
216249
- process.exit(0);
216250
216392
  } catch (err) {
216251
216393
  console.error("Failed to save config:", err);
216252
216394
  process.exit(1);
216253
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);
216254
216410
  }
216255
216411
 
216256
216412
  // src/commands/doctor.ts
@@ -216282,33 +216438,27 @@ async function doctorCommand() {
216282
216438
  async function getMe() {
216283
216439
  if (meCache) return meCache;
216284
216440
  try {
216285
- meCache = await api.get("/me");
216441
+ meCache = await api.get("/me", { timeoutMs: 8e3, retries: 1 });
216286
216442
  } catch {
216287
216443
  meCache = null;
216288
216444
  }
216289
216445
  return meCache;
216290
216446
  }
216291
- let reposCache = null;
216292
- async function getRepos(slug) {
216293
- if (reposCache) return reposCache;
216294
- try {
216295
- reposCache = await api.get(`/workspaces/${slug}/repos`);
216296
- } catch {
216297
- reposCache = null;
216298
- }
216299
- return reposCache;
216300
- }
216301
- if (apiKey && apiUrl !== "http://localhost:3000/api/v1") {
216302
- const me = await getMe();
216303
- if (me?.slug) {
216304
- checks.push({ name: "Workspace", status: "ok", detail: me.slug });
216305
- } else {
216306
- checks.push({ name: "Workspace", status: "warn", detail: "API returned no workspace. Run `driftless init`" });
216307
- }
216308
- } else {
216447
+ const resolution = await resolveRepo();
216448
+ const localOrNoKey = !apiKey || apiUrl === "http://localhost:3000/api/v1";
216449
+ if (localOrNoKey) {
216309
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)" });
216310
216461
  }
216311
- const resolution = await resolveRepo();
216312
216462
  if (resolution.ok) {
216313
216463
  checks.push({ name: "Repo linked", status: "ok", detail: `${resolution.remote.org}/${resolution.remote.repo}` });
216314
216464
  checks.push({
@@ -216377,6 +216527,10 @@ async function doctorCommand() {
216377
216527
  }
216378
216528
  console.log(`
216379
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
+ }
216380
216534
  if (failCount > 0) {
216381
216535
  console.log("\nFix failures before running `driftless init` or `driftless scan`.");
216382
216536
  process.exit(1);
@@ -216389,7 +216543,7 @@ function pad2(s, n) {
216389
216543
  }
216390
216544
 
216391
216545
  // src/index.ts
216392
- var VERSION = "0.1.37";
216546
+ var VERSION = "0.1.39";
216393
216547
  var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
216394
216548
 
216395
216549
  Install: npm install -g @driftless-sh/cli