@dosu/cli 0.59.0 → 0.60.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 (3) hide show
  1. package/README.md +6 -4
  2. package/bin/dosu.js +154 -72
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -206,10 +206,12 @@ installation history is never aliased to an account. Setup-funnel events are lin
206
206
  and may include documented coarse setup choices. `DO_NOT_TRACK=1` and
207
207
  `DOSU_TELEMETRY_DISABLED=1` disable all telemetry for the process. Dosu never collects
208
208
  prompts, raw command lines, free-form argument or option values, user source code, file contents,
209
- local paths, raw environment-variable names or values, credentials, raw error messages, or
210
- `debug.log`. See
211
- [Telemetry and privacy](docs/telemetry.md) for the exact event fields, destinations, retention, and
212
- controls.
209
+ local paths, credentials, raw error messages, or `debug.log`. Environment-variable names and values
210
+ are not collected, with one exception: when a managed Claude Code settings conflict prevents a
211
+ study run, Dosu reports the allowlisted public Claude Code setting/variable names that caused the
212
+ conflict (never their values) to help diagnose why knowledge sync fails due to settings conflicts.
213
+ See [Telemetry and privacy](docs/telemetry.md) for the exact event fields, destinations, retention,
214
+ and controls.
213
215
 
214
216
  ## Configuration
215
217
 
package/bin/dosu.js CHANGED
@@ -1083,7 +1083,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1083
1083
  function getVersionString() {
1084
1084
  return `v${VERSION}`;
1085
1085
  }
1086
- var VERSION = "0.59.0", INSTALL_CHANNEL = "npm";
1086
+ var VERSION = "0.60.0", INSTALL_CHANNEL = "npm";
1087
1087
 
1088
1088
  // src/debug/logger.ts
1089
1089
  import {
@@ -13741,12 +13741,70 @@ var init_sync = __esm(() => {
13741
13741
  init_watermark();
13742
13742
  });
13743
13743
 
13744
+ // src/learner/conflicts.ts
13745
+ import { existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
13746
+ function managedSettingsPaths(platform = process.platform) {
13747
+ switch (platform) {
13748
+ case "darwin":
13749
+ return ["/Library/Application Support/ClaudeCode/managed-settings.json"];
13750
+ case "win32":
13751
+ return ["C:\\ProgramData\\ClaudeCode\\managed-settings.json"];
13752
+ default:
13753
+ return ["/etc/claude-code/managed-settings.json"];
13754
+ }
13755
+ }
13756
+ function conflictingKeysIn(settings) {
13757
+ const keys = [];
13758
+ for (const key of CONFLICTING_KEYS) {
13759
+ if (key in settings)
13760
+ keys.push(key);
13761
+ }
13762
+ const env = settings.env;
13763
+ if (typeof env === "object" && env !== null && !Array.isArray(env)) {
13764
+ for (const name of Object.keys(env)) {
13765
+ if (CONFLICTING_ENV_PREFIXES.some((p) => name.startsWith(p)))
13766
+ keys.push(`env.${name}`);
13767
+ }
13768
+ }
13769
+ return keys;
13770
+ }
13771
+ function detectSettingsConflicts(paths = managedSettingsPaths()) {
13772
+ const conflicts = [];
13773
+ for (const file of paths) {
13774
+ if (!existsSync13(file))
13775
+ continue;
13776
+ let settings;
13777
+ try {
13778
+ settings = JSON.parse(readFileSync15(file, "utf8"));
13779
+ } catch {
13780
+ conflicts.push({ file, keys: [UNREADABLE_SETTINGS_KEY] });
13781
+ continue;
13782
+ }
13783
+ if (typeof settings !== "object" || settings === null)
13784
+ continue;
13785
+ const keys = conflictingKeysIn(settings);
13786
+ if (keys.length > 0)
13787
+ conflicts.push({ file, keys });
13788
+ }
13789
+ return conflicts;
13790
+ }
13791
+ var CONFLICTING_KEYS, CONFLICTING_ENV_PREFIXES, UNREADABLE_SETTINGS_KEY = "<unreadable or invalid JSON>";
13792
+ var init_conflicts = __esm(() => {
13793
+ CONFLICTING_KEYS = [
13794
+ "apiKeyHelper",
13795
+ "forceLoginMethod",
13796
+ "awsAuthRefresh",
13797
+ "awsCredentialExport"
13798
+ ];
13799
+ CONFLICTING_ENV_PREFIXES = ["ANTHROPIC_", "CLAUDE_CODE_", "AWS_"];
13800
+ });
13801
+
13744
13802
  // src/telemetry/debug-id.ts
13745
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "ecb91fd5-693a-ffc0-6475-6e2164756e21";
13803
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "bd089d5e-a4ab-0f80-6475-6e2164756e21";
13746
13804
 
13747
13805
  // src/telemetry/telemetry.ts
13748
13806
  import { randomUUID as randomUUID2 } from "node:crypto";
13749
- import { existsSync as existsSync13 } from "node:fs";
13807
+ import { existsSync as existsSync14 } from "node:fs";
13750
13808
  import { request as httpRequest2 } from "node:http";
13751
13809
  import { request as httpsRequest } from "node:https";
13752
13810
  import { isAbsolute, relative, resolve as resolve2, sep } from "node:path";
@@ -13884,7 +13942,7 @@ function parseOwnedStackLine(originalLine, packageRoot) {
13884
13942
  return;
13885
13943
  }
13886
13944
  try {
13887
- if (!existsSync13(resolvedPath))
13945
+ if (!existsSync14(resolvedPath))
13888
13946
  return;
13889
13947
  } catch {
13890
13948
  return;
@@ -13970,6 +14028,29 @@ function allowlisted(value, allowed) {
13970
14028
  function matching(value, pattern) {
13971
14029
  return typeof value === "string" && pattern.test(value) ? value : undefined;
13972
14030
  }
14031
+ function conflictKeysFacet(value) {
14032
+ if (!Array.isArray(value))
14033
+ return;
14034
+ const keys = new Set;
14035
+ for (const raw of value) {
14036
+ if (typeof raw !== "string")
14037
+ continue;
14038
+ if (SETTINGS_CONFLICT_KEYS.has(raw))
14039
+ keys.add(raw);
14040
+ else if (raw === UNREADABLE_SETTINGS_KEY)
14041
+ keys.add("unreadable");
14042
+ else
14043
+ keys.add(SETTINGS_CONFLICT_FAMILIES.find(([p]) => raw.startsWith(p))?.[1] ?? "other");
14044
+ }
14045
+ let joined = "";
14046
+ for (const key of [...keys].sort()) {
14047
+ const next = joined ? `${joined},${key}` : key;
14048
+ if (next.length > MAX_CONFLICT_KEYS_LENGTH)
14049
+ break;
14050
+ joined = next;
14051
+ }
14052
+ return joined || undefined;
14053
+ }
13973
14054
  function sanitizeFacets(facets) {
13974
14055
  if (!facets || typeof facets !== "object")
13975
14056
  return {};
@@ -13977,6 +14058,7 @@ function sanitizeFacets(facets) {
13977
14058
  const status = allowlisted(facets.sync_status, SYNC_STATUSES);
13978
14059
  const learner = allowlisted(facets.learner_outcome, LEARNER_OUTCOMES);
13979
14060
  const gatewayReason = allowlisted(facets.gateway_reason, GATEWAY_REASONS);
14061
+ const conflictKeys = conflictKeysFacet(facets.settings_conflict_keys);
13980
14062
  const claudeSource = allowlisted(facets.claude_code_source, CLAUDE_CODE_SOURCES);
13981
14063
  const claudeVersion = matching(facets.claude_code_version, CLAUDE_CODE_VERSION_PATTERN);
13982
14064
  const model = matching(facets.learner_model, LEARNER_MODEL_PATTERN);
@@ -13988,6 +14070,7 @@ function sanitizeFacets(facets) {
13988
14070
  ...facets.notes_written !== undefined ? { notes_written: countBucket(facets.notes_written) } : {},
13989
14071
  ...learner ? { learner_outcome: learner } : {},
13990
14072
  ...gatewayReason ? { gateway_reason: gatewayReason } : {},
14073
+ ...conflictKeys ? { settings_conflict_keys: conflictKeys } : {},
13991
14074
  ...claudeSource ? { claude_code_source: claudeSource } : {},
13992
14075
  ...claudeVersion ? { claude_code_version: claudeVersion } : {},
13993
14076
  ...model ? { learner_model: model } : {},
@@ -14115,7 +14198,8 @@ function exceptionSummary(command, error, facets) {
14115
14198
  const outcome = facets.learner_outcome ?? facets.sync_status;
14116
14199
  if (!outcome)
14117
14200
  return error.code ?? error.type;
14118
- return `${command}: ${outcome}${facets.gateway_reason ? ` (${facets.gateway_reason})` : ""}`;
14201
+ const detail = facets.gateway_reason ?? facets.settings_conflict_keys;
14202
+ return `${command}: ${outcome}${detail ? ` (${detail})` : ""}`;
14119
14203
  }
14120
14204
  function buildSentryEnvelope(input) {
14121
14205
  const parsedDsn = parseSentryDsn(input.dsn);
@@ -14517,8 +14601,9 @@ function createCommandTelemetry(settings, dependencies = {}) {
14517
14601
  function shouldSendToSentry(error) {
14518
14602
  return !["SESSION_EXPIRED", "SESSION_PERSISTENCE_ERROR"].includes(error.code ?? "");
14519
14603
  }
14520
- var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES, SYNC_TRIGGERS, SYNC_STATUSES, LEARNER_OUTCOMES, GATEWAY_REASONS, CLAUDE_CODE_SOURCES, CLAUDE_CODE_VERSION_PATTERN, LEARNER_MODEL_PATTERN, BACKFILL_OFFERS, SENTRY_FACET_TAGS, pendingFacets;
14604
+ var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES, SYNC_TRIGGERS, SYNC_STATUSES, LEARNER_OUTCOMES, GATEWAY_REASONS, CLAUDE_CODE_SOURCES, SETTINGS_CONFLICT_KEYS, SETTINGS_CONFLICT_FAMILIES, MAX_CONFLICT_KEYS_LENGTH = 200, CLAUDE_CODE_VERSION_PATTERN, LEARNER_MODEL_PATTERN, BACKFILL_OFFERS, SENTRY_FACET_TAGS, pendingFacets;
14521
14605
  var init_telemetry = __esm(() => {
14606
+ init_conflicts();
14522
14607
  MAX_RESPONSE_BYTES = 1024 * 1024;
14523
14608
  CLI_PACKAGE_ROOT = detectCliPackageRoot();
14524
14609
  SAFE_ERROR_TYPES = new Set([
@@ -14635,6 +14720,58 @@ var init_telemetry = __esm(() => {
14635
14720
  "other"
14636
14721
  ]);
14637
14722
  CLAUDE_CODE_SOURCES = new Set(["sdk", "system", "missing"]);
14723
+ SETTINGS_CONFLICT_KEYS = new Set([
14724
+ "apiKeyHelper",
14725
+ "forceLoginMethod",
14726
+ "awsAuthRefresh",
14727
+ "awsCredentialExport",
14728
+ "env.ANTHROPIC_API_KEY",
14729
+ "env.ANTHROPIC_AUTH_TOKEN",
14730
+ "env.ANTHROPIC_BASE_URL",
14731
+ "env.ANTHROPIC_CUSTOM_HEADERS",
14732
+ "env.ANTHROPIC_MODEL",
14733
+ "env.ANTHROPIC_SMALL_FAST_MODEL",
14734
+ "env.ANTHROPIC_DEFAULT_OPUS_MODEL",
14735
+ "env.ANTHROPIC_DEFAULT_SONNET_MODEL",
14736
+ "env.ANTHROPIC_DEFAULT_HAIKU_MODEL",
14737
+ "env.ANTHROPIC_BEDROCK_BASE_URL",
14738
+ "env.ANTHROPIC_VERTEX_BASE_URL",
14739
+ "env.ANTHROPIC_VERTEX_PROJECT_ID",
14740
+ "env.ANTHROPIC_FOUNDRY_BASE_URL",
14741
+ "env.ANTHROPIC_FOUNDRY_API_KEY",
14742
+ "env.ANTHROPIC_AWS_BASE_URL",
14743
+ "env.ANTHROPIC_BEDROCK_MANTLE_BASE_URL",
14744
+ "env.ANTHROPIC_AWS_API_KEY",
14745
+ "env.ANTHROPIC_PROFILE",
14746
+ "env.ANTHROPIC_ENVIRONMENT_KEY",
14747
+ "env.ANTHROPIC_IDENTITY_TOKEN",
14748
+ "env.ANTHROPIC_FEDERATION_RULE_ID",
14749
+ "env.ANTHROPIC_SERVICE_ACCOUNT_ID",
14750
+ "env.CLAUDE_CODE_USE_BEDROCK",
14751
+ "env.CLAUDE_CODE_USE_VERTEX",
14752
+ "env.CLAUDE_CODE_USE_FOUNDRY",
14753
+ "env.CLAUDE_CODE_USE_MANTLE",
14754
+ "env.CLAUDE_CODE_USE_ANTHROPIC_AWS",
14755
+ "env.CLAUDE_CODE_OAUTH_TOKEN",
14756
+ "env.CLAUDE_CODE_API_BASE_URL",
14757
+ "env.CLAUDE_CODE_SKIP_BEDROCK_AUTH",
14758
+ "env.CLAUDE_CODE_SKIP_VERTEX_AUTH",
14759
+ "env.CLAUDE_CODE_API_KEY_HELPER_TTL_MS",
14760
+ "env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC",
14761
+ "env.CLAUDE_CODE_ENABLE_TELEMETRY",
14762
+ "env.CLAUDE_CODE_MAX_OUTPUT_TOKENS",
14763
+ "env.AWS_REGION",
14764
+ "env.AWS_PROFILE",
14765
+ "env.AWS_BEARER_TOKEN_BEDROCK",
14766
+ "env.AWS_ACCESS_KEY_ID",
14767
+ "env.AWS_SECRET_ACCESS_KEY",
14768
+ "env.AWS_SESSION_TOKEN"
14769
+ ]);
14770
+ SETTINGS_CONFLICT_FAMILIES = [
14771
+ ["env.ANTHROPIC_", "env.ANTHROPIC_other"],
14772
+ ["env.CLAUDE_CODE_", "env.CLAUDE_CODE_other"],
14773
+ ["env.AWS_", "env.AWS_other"]
14774
+ ];
14638
14775
  CLAUDE_CODE_VERSION_PATTERN = /^\d{1,4}\.\d{1,4}\.\d{1,6}(?:-[0-9A-Za-z]{1,16}(?:\.[0-9A-Za-z]{1,16}){0,3})?$/;
14639
14776
  LEARNER_MODEL_PATTERN = /^claude-[a-z0-9.-]{1,56}$/;
14640
14777
  BACKFILL_OFFERS = new Set([
@@ -14649,6 +14786,7 @@ var init_telemetry = __esm(() => {
14649
14786
  "sync_status",
14650
14787
  "learner_outcome",
14651
14788
  "gateway_reason",
14789
+ "settings_conflict_keys",
14652
14790
  "claude_code_source",
14653
14791
  "claude_code_version",
14654
14792
  "learner_model"
@@ -14682,7 +14820,7 @@ var init_agent_select = __esm(() => {
14682
14820
  });
14683
14821
 
14684
14822
  // src/incognito/agents.ts
14685
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync15, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "node:fs";
14823
+ import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync16, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "node:fs";
14686
14824
  import { homedir as homedir4 } from "node:os";
14687
14825
  import { dirname as dirname5, join as join13 } from "node:path";
14688
14826
  function withFrontmatter(body) {
@@ -14707,18 +14845,18 @@ function fileAgent(options) {
14707
14845
  commandPath,
14708
14846
  isEnabled: () => {
14709
14847
  const path = commandPath();
14710
- if (!existsSync14(path))
14848
+ if (!existsSync15(path))
14711
14849
  return false;
14712
14850
  try {
14713
- return readFileSync15(path, "utf-8").includes(INCOGNITO_MARKER);
14851
+ return readFileSync16(path, "utf-8").includes(INCOGNITO_MARKER);
14714
14852
  } catch {
14715
14853
  return false;
14716
14854
  }
14717
14855
  },
14718
14856
  enable: () => {
14719
14857
  const path = commandPath();
14720
- if (existsSync14(path)) {
14721
- if (readFileSync15(path, "utf-8") === content)
14858
+ if (existsSync15(path)) {
14859
+ if (readFileSync16(path, "utf-8") === content)
14722
14860
  return "unchanged";
14723
14861
  writeFileSync10(path, content, "utf-8");
14724
14862
  return "updated";
@@ -14729,7 +14867,7 @@ function fileAgent(options) {
14729
14867
  },
14730
14868
  disable: () => {
14731
14869
  const path = commandPath();
14732
- if (!existsSync14(path))
14870
+ if (!existsSync15(path))
14733
14871
  return "not_found";
14734
14872
  unlinkSync2(path);
14735
14873
  return "removed";
@@ -15171,64 +15309,6 @@ function createRunConfigDir() {
15171
15309
  }
15172
15310
  var init_config_dir = () => {};
15173
15311
 
15174
- // src/learner/conflicts.ts
15175
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "node:fs";
15176
- function managedSettingsPaths(platform = process.platform) {
15177
- switch (platform) {
15178
- case "darwin":
15179
- return ["/Library/Application Support/ClaudeCode/managed-settings.json"];
15180
- case "win32":
15181
- return ["C:\\ProgramData\\ClaudeCode\\managed-settings.json"];
15182
- default:
15183
- return ["/etc/claude-code/managed-settings.json"];
15184
- }
15185
- }
15186
- function conflictingKeysIn(settings) {
15187
- const keys = [];
15188
- for (const key of CONFLICTING_KEYS) {
15189
- if (key in settings)
15190
- keys.push(key);
15191
- }
15192
- const env = settings.env;
15193
- if (typeof env === "object" && env !== null && !Array.isArray(env)) {
15194
- for (const name of Object.keys(env)) {
15195
- if (CONFLICTING_ENV_PREFIXES.some((p) => name.startsWith(p)))
15196
- keys.push(`env.${name}`);
15197
- }
15198
- }
15199
- return keys;
15200
- }
15201
- function detectSettingsConflicts(paths = managedSettingsPaths()) {
15202
- const conflicts = [];
15203
- for (const file of paths) {
15204
- if (!existsSync15(file))
15205
- continue;
15206
- let settings;
15207
- try {
15208
- settings = JSON.parse(readFileSync16(file, "utf8"));
15209
- } catch {
15210
- conflicts.push({ file, keys: ["<unreadable or invalid JSON>"] });
15211
- continue;
15212
- }
15213
- if (typeof settings !== "object" || settings === null)
15214
- continue;
15215
- const keys = conflictingKeysIn(settings);
15216
- if (keys.length > 0)
15217
- conflicts.push({ file, keys });
15218
- }
15219
- return conflicts;
15220
- }
15221
- var CONFLICTING_KEYS, CONFLICTING_ENV_PREFIXES;
15222
- var init_conflicts = __esm(() => {
15223
- CONFLICTING_KEYS = [
15224
- "apiKeyHelper",
15225
- "forceLoginMethod",
15226
- "awsAuthRefresh",
15227
- "awsCredentialExport"
15228
- ];
15229
- CONFLICTING_ENV_PREFIXES = ["ANTHROPIC_", "CLAUDE_CODE_", "AWS_"];
15230
- });
15231
-
15232
15312
  // src/learner/env.ts
15233
15313
  function isStripped(name) {
15234
15314
  return STRIPPED_NAMES.has(name) || STRIPPED_PREFIXES.some((p) => name.startsWith(p));
@@ -58365,7 +58445,8 @@ async function runLearner(options) {
58365
58445
  outcome: "settings_conflict",
58366
58446
  notesWritten: 0,
58367
58447
  turns: 0,
58368
- message: `Refusing to run: conflicting Claude Code settings would override the study run's auth (${detail})`
58448
+ message: `Refusing to run: conflicting Claude Code settings would override the study run's auth (${detail})`,
58449
+ conflictKeys: conflicts.flatMap((c) => c.keys)
58369
58450
  };
58370
58451
  }
58371
58452
  const gatewayURL = options.gatewayURL ?? getLlmGatewayURL();
@@ -58803,6 +58884,7 @@ function learnerFacets(learner) {
58803
58884
  return {
58804
58885
  learner_outcome: learner.outcome,
58805
58886
  ...learner.gatewayReason ? { gateway_reason: learner.gatewayReason } : {},
58887
+ ...learner.conflictKeys ? { settings_conflict_keys: learner.conflictKeys } : {},
58806
58888
  ...learner.claudeCodeSource ? { claude_code_source: learner.claudeCodeSource } : {},
58807
58889
  ...learner.claudeCodeVersion ? { claude_code_version: learner.claudeCodeVersion } : {},
58808
58890
  ...learner.model ? { learner_model: learner.model } : {}
@@ -67761,4 +67843,4 @@ main().catch((err) => {
67761
67843
  process.exit(1);
67762
67844
  });
67763
67845
 
67764
- //# debugId=ecb91fd5-693a-ffc0-6475-6e2164756e21
67846
+ //# debugId=bd089d5e-a4ab-0f80-6475-6e2164756e21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",