claude-nomad 0.63.0 → 0.63.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.63.2](https://github.com/funkadelic/claude-nomad/compare/v0.63.1...v0.63.2) (2026-08-04)
4
+
5
+ ### What's new
6
+
7
+ - **`nomad doctor` stops warning about settings you legitimately set.** The list of settings it
8
+ recognizes has been updated and synced with the current Claude Code settings schema, so settings
9
+ the app actually writes (quiet hours, the break reminder, the token-usage reminder, and others)
10
+ are no longer reported as unknown keys on an otherwise healthy machine. Entries that were never
11
+ settings at all have been dropped at the same time.
12
+ - **A new Claude Code setting is recognized within a day.** The job that tracks the official
13
+ settings list ran once a week, so a setting added on a Tuesday could be reported as unknown until
14
+ the following Monday. It now runs daily.
15
+ - **The list keeps its own bookkeeping straight from here.** When a setting Claude Code writes
16
+ ahead of its published documentation is later documented, it now moves across on its own instead
17
+ of being recorded in two places at once.
18
+
19
+
20
+ ### Fixed
21
+
22
+ * **doctor:** refresh APP_ONLY_KEYS against the current settings schema ([#487](https://github.com/funkadelic/claude-nomad/issues/487)) ([1102b41](https://github.com/funkadelic/claude-nomad/commit/1102b4180fd53b90e869dc00736b3e62c9a997cc))
23
+
24
+
25
+ ### Changed
26
+
27
+ * **doctor:** sync settings schema keys ([#483](https://github.com/funkadelic/claude-nomad/issues/483)) ([c406281](https://github.com/funkadelic/claude-nomad/commit/c4062811e6568f4e3d1c1be44a090de8df810385))
28
+ * run the settings schema drift check daily ([#486](https://github.com/funkadelic/claude-nomad/issues/486)) ([090f247](https://github.com/funkadelic/claude-nomad/commit/090f247d5e941d08300138967560e09487f6c387))
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * bump npm-run-all2 from 9.0.2 to 9.0.3 in the dev-dependencies group ([#480](https://github.com/funkadelic/claude-nomad/issues/480)) ([9e9e7ef](https://github.com/funkadelic/claude-nomad/commit/9e9e7efcc586deafd1ce03224b88ffe8e7a0d023))
34
+ * bump the codeql-action group across 1 directory with 2 updates ([#481](https://github.com/funkadelic/claude-nomad/issues/481)) ([0905267](https://github.com/funkadelic/claude-nomad/commit/0905267f2a74c23a41bde5947edaa9b823d59940))
35
+ * bump the prod-dependencies group across 1 directory with 2 updates ([#485](https://github.com/funkadelic/claude-nomad/issues/485)) ([d77491d](https://github.com/funkadelic/claude-nomad/commit/d77491d241b9faf2cacb87b54d58591ca1dd3bef))
36
+
37
+ ## [0.63.1](https://github.com/funkadelic/claude-nomad/compare/v0.63.0...v0.63.1) (2026-07-31)
38
+
39
+
40
+ ### Fixed
41
+
42
+ * **push:** correct gitleaks column offsets in recovery prompts ([#478](https://github.com/funkadelic/claude-nomad/issues/478)) ([e230993](https://github.com/funkadelic/claude-nomad/commit/e2309932456f5646694685dd0c3b6b6e1007059f))
43
+
3
44
  ## [0.63.0](https://github.com/funkadelic/claude-nomad/compare/v0.62.6...v0.63.0) (2026-07-31)
4
45
 
5
46
  ### What's new
package/dist/nomad.mjs CHANGED
@@ -353,8 +353,11 @@ var init_settings_keys = __esm({
353
353
  "allowManagedPermissionRulesOnly",
354
354
  "alwaysThinkingEnabled",
355
355
  "apiKeyHelper",
356
+ "askUserQuestionTimeout",
356
357
  "attribution",
357
358
  "autoCompactEnabled",
359
+ "autoConnectIde",
360
+ "autoInstallIdeExtension",
358
361
  "autoMemoryDirectory",
359
362
  "autoMemoryEnabled",
360
363
  "autoMode",
@@ -366,6 +369,7 @@ var init_settings_keys = __esm({
366
369
  "awsCredentialExport",
367
370
  "axScreenReader",
368
371
  "blockedMarketplaces",
372
+ "browserExternalPageTools",
369
373
  "channelsEnabled",
370
374
  "claudeMd",
371
375
  "claudeMdExcludes",
@@ -373,24 +377,31 @@ var init_settings_keys = __esm({
373
377
  "companyAnnouncements",
374
378
  "defaultShell",
375
379
  "deniedMcpServers",
380
+ "diffTool",
376
381
  "disableAgentView",
377
382
  "disableAllHooks",
378
383
  "disableArtifact",
379
384
  "disableAutoMode",
385
+ "disableBrowserExternalNavigation",
380
386
  "disableBundledSkills",
381
387
  "disableClaudeAiConnectors",
382
388
  "disableDeepLinkRegistration",
383
389
  "disabledMcpjsonServers",
390
+ "disableMobileSimulatorTools",
384
391
  "disableRemoteControl",
392
+ "disableSideloadFlags",
385
393
  "disableSkillShellExecution",
386
394
  "disableWorkflows",
387
395
  "editorMode",
388
396
  "effortLevel",
397
+ "emojiCompletionEnabled",
389
398
  "enableAllProjectMcpServers",
399
+ "enableArtifact",
390
400
  "enabledMcpjsonServers",
391
401
  "enabledPlugins",
392
402
  "enforceAvailableModels",
393
403
  "env",
404
+ "externalEditorContext",
394
405
  "extraKnownMarketplaces",
395
406
  "fallbackModel",
396
407
  "fastMode",
@@ -399,6 +410,7 @@ var init_settings_keys = __esm({
399
410
  "fileCheckpointingEnabled",
400
411
  "fileSuggestion",
401
412
  "footerLinksRegexes",
413
+ "forceLoginGatewayUrl",
402
414
  "forceLoginMethod",
403
415
  "forceLoginOrgUUID",
404
416
  "forceRemoteSettingsRefresh",
@@ -411,13 +423,13 @@ var init_settings_keys = __esm({
411
423
  "language",
412
424
  "leftArrowOpensAgents",
413
425
  "managedMcpServers",
414
- "maxSkillDescriptionChars",
415
426
  "minimumVersion",
416
427
  "model",
417
428
  "modelOverrides",
418
429
  "otelHeadersHelper",
419
430
  "outputStyle",
420
431
  "parentSettingsBehavior",
432
+ "permissionExplainerEnabled",
421
433
  "permissions",
422
434
  "plansDirectory",
423
435
  "pluginConfigs",
@@ -426,8 +438,10 @@ var init_settings_keys = __esm({
426
438
  "policyHelper",
427
439
  "preferredNotifChannel",
428
440
  "prefersReducedMotion",
441
+ "processWrapper",
429
442
  "prUrlTemplate",
430
443
  "remoteControlAtStartup",
444
+ "requireCoworkFullVmSandbox",
431
445
  "requiredMaximumVersion",
432
446
  "requiredMinimumVersion",
433
447
  "respectGitignore",
@@ -437,6 +451,7 @@ var init_settings_keys = __esm({
437
451
  "showThinkingSummaries",
438
452
  "showTurnDuration",
439
453
  "skillListingBudgetFraction",
454
+ "skillListingMaxDescChars",
440
455
  "skillOverrides",
441
456
  "skipDangerousModePermissionPrompt",
442
457
  "skippedMarketplaces",
@@ -452,6 +467,7 @@ var init_settings_keys = __esm({
452
467
  "strictPluginOnlyCustomization",
453
468
  "subagentStatusLine",
454
469
  "syntaxHighlightingDisabled",
470
+ "teammateDefaultModel",
455
471
  "teammateMode",
456
472
  "terminalProgressBarEnabled",
457
473
  "theme",
@@ -459,29 +475,41 @@ var init_settings_keys = __esm({
459
475
  "useAutoModeDuringPlan",
460
476
  "verbose",
461
477
  "viewMode",
478
+ "vimInsertModeRemaps",
462
479
  "voice",
463
480
  "voiceEnabled",
464
481
  "wheelScrollAccelerationEnabled",
465
482
  "workflowKeywordTriggerEnabled",
483
+ "workflowSizeGuideline",
466
484
  "worktree",
467
485
  "wslInheritsWindowsSettings"
468
486
  ];
469
487
  APP_ONLY_KEYS = [
470
- "agents",
471
- "apiKeyHelperTimeoutMs",
472
- "awsLoginRefresh",
473
- "awsRegion",
474
- "awsRetryMode",
475
- "disableNonEssentialModelCalls",
476
- "enabledExperimentalFeatures",
477
- "installMethod",
478
- "pluginGroups",
479
- "pluginRepositoryEnabled",
480
- "pluginsLocalConfig",
481
- "proxy",
488
+ "autoCompactWindow",
489
+ "autoDreamEnabled",
490
+ "autoUploadSessions",
491
+ "breakReminder",
492
+ "daemonColdStart",
493
+ "defaultView",
494
+ "doneMeansMerged",
495
+ "enableWorkflows",
496
+ "feedbackDrafts",
497
+ "isolatePeerMachines",
498
+ "precomputeCompactionEnabled",
499
+ "promptSuggestionEnabled",
500
+ "proxyAuthHelper",
501
+ "quietHours",
502
+ "remote",
503
+ "showMessageTimestamps",
482
504
  "skipAutoPermissionPrompt",
483
- "statsig",
484
- "subagents"
505
+ "skipWorkflowUsageWarning",
506
+ "switchModelsOnFlag",
507
+ "terminalTitleFromRename",
508
+ "todoFeatureEnabled",
509
+ "totalTokensReminder",
510
+ "totalTokensReminderAfterUserTurn",
511
+ "totalTokensReminderBudget",
512
+ "ultracode"
485
513
  ];
486
514
  KNOWN_SETTINGS_KEYS = /* @__PURE__ */ new Set([...SCHEMA_KEYS, ...APP_ONLY_KEYS]);
487
515
  }
@@ -5658,6 +5686,93 @@ function dropSessionFromStaged(sid, map) {
5658
5686
  init_push_gitleaks_scan();
5659
5687
  init_utils();
5660
5688
 
5689
+ // src/commands.push.recovery.span-align.ts
5690
+ var REDACTED_TOKEN = "REDACTED";
5691
+ var SHIFT_WINDOW = [0, -1, 1, -2, 2];
5692
+ var ENTROPY_TOLERANCE = 1e-4;
5693
+ function byteAt(buf, at) {
5694
+ return at >= 0 && at < buf.length ? buf[at] : void 0;
5695
+ }
5696
+ function isWordByte(byte) {
5697
+ if (byte === void 0) return false;
5698
+ return byte >= 48 && byte <= 57 || byte >= 65 && byte <= 90 || byte >= 97 && byte <= 122 || byte === 95;
5699
+ }
5700
+ function splitsToken(buf, at) {
5701
+ return isWordByte(byteAt(buf, at - 1)) && isWordByte(byteAt(buf, at));
5702
+ }
5703
+ function shannonEntropy(text) {
5704
+ if (text.length === 0) return 0;
5705
+ const counts = /* @__PURE__ */ new Map();
5706
+ for (const ch of text) counts.set(ch, (counts.get(ch) ?? 0) + 1);
5707
+ let bits = 0;
5708
+ for (const n of counts.values()) {
5709
+ const p = n / text.length;
5710
+ bits -= p * Math.log2(p);
5711
+ }
5712
+ return bits;
5713
+ }
5714
+ function matchTemplate(finding) {
5715
+ if (finding.Match.length === 0) return null;
5716
+ const idx = finding.Match.indexOf(REDACTED_TOKEN);
5717
+ if (idx < 0) return { pre: "", post: "", redacted: false };
5718
+ return {
5719
+ pre: finding.Match.slice(0, idx),
5720
+ post: finding.Match.slice(idx + REDACTED_TOKEN.length),
5721
+ redacted: true
5722
+ };
5723
+ }
5724
+ function fitsTemplate(span, finding, template) {
5725
+ if (!template.redacted) return span === finding.Match;
5726
+ return span.startsWith(template.pre) && span.endsWith(template.post);
5727
+ }
5728
+ function candidateAt(finding, buf, template, shift) {
5729
+ const start = finding.StartColumn - 1 + shift;
5730
+ const end = start + (finding.EndColumn - finding.StartColumn + 1);
5731
+ if (start < 0 || end > buf.length) return null;
5732
+ const span = buf.subarray(start, end).toString("utf8");
5733
+ if (!fitsTemplate(span, finding, template)) return null;
5734
+ const valueStart = start + Buffer.byteLength(template.pre, "utf8");
5735
+ const valueEnd = end - Buffer.byteLength(template.post, "utf8");
5736
+ const value = buf.subarray(valueStart, valueEnd).toString("utf8");
5737
+ if (value.length === 0) return null;
5738
+ if (splitsToken(buf, start) || splitsToken(buf, end)) return null;
5739
+ return { start, end, valueStart, value, exact: template.redacted };
5740
+ }
5741
+ function locateSecretLiteral(finding, buf) {
5742
+ const secret = finding.Secret;
5743
+ if (typeof secret !== "string" || secret.length === 0 || secret === REDACTED_TOKEN) return null;
5744
+ const needle = Buffer.from(secret, "utf8");
5745
+ const hint = finding.StartColumn - 1;
5746
+ let best = null;
5747
+ for (let at = buf.indexOf(needle); at >= 0; at = buf.indexOf(needle, at + 1)) {
5748
+ if (best === null || Math.abs(at - hint) < Math.abs(best - hint)) best = at;
5749
+ }
5750
+ if (best === null) return null;
5751
+ return { start: best, end: best + needle.length, valueStart: best, value: secret, exact: true };
5752
+ }
5753
+ function breakTie(hits, finding) {
5754
+ const reported = finding.Entropy;
5755
+ if (typeof reported !== "number" || !Number.isFinite(reported) || reported <= 0) return null;
5756
+ const agreeing = hits.filter(
5757
+ (h2) => h2.exact && Math.abs(shannonEntropy(h2.value) - reported) <= ENTROPY_TOLERANCE
5758
+ );
5759
+ return agreeing.length === 1 ? agreeing[0] : null;
5760
+ }
5761
+ function alignFindingSpan(finding, buf) {
5762
+ const literal = locateSecretLiteral(finding, buf);
5763
+ if (literal !== null) return literal;
5764
+ if (typeof finding.EndLine === "number" && finding.EndLine > finding.StartLine) return null;
5765
+ const template = matchTemplate(finding);
5766
+ if (template === null) return null;
5767
+ const hits = [];
5768
+ for (const shift of SHIFT_WINDOW) {
5769
+ const candidate = candidateAt(finding, buf, template, shift);
5770
+ if (candidate !== null) hits.push(candidate);
5771
+ }
5772
+ if (hits.length === 1) return hits[0];
5773
+ return breakTie(hits, finding);
5774
+ }
5775
+
5661
5776
  // src/commands.push.recovery.secret-shape.ts
5662
5777
  var VALUE_HEAD = 4;
5663
5778
  var VALUE_TAIL = 4;
@@ -5665,7 +5780,7 @@ var MIN_ELIDE_LEN = 16;
5665
5780
  var FRAGMENT_SEP = "...";
5666
5781
  var NEAR_WINDOW = 40;
5667
5782
  var VALUE_PAD = 16;
5668
- var REDACTED_TOKEN = "REDACTED";
5783
+ var SIBLING_PAD = 2;
5669
5784
  var CONTROL_CHARS = /[\x00-\x1f\x7f]/g;
5670
5785
  function classifyCharset(value) {
5671
5786
  if (/^[0-9a-fA-F]+$/.test(value)) return "hex";
@@ -5691,26 +5806,23 @@ function withoutValue(near, value) {
5691
5806
  const idx = near.indexOf(value);
5692
5807
  return idx < 0 ? near : emptyToNull(near.slice(0, idx));
5693
5808
  }
5694
- function alignRedactedMatch(finding, span, lead) {
5809
+ function templateLead(finding) {
5695
5810
  const idx = finding.Match.indexOf(REDACTED_TOKEN);
5696
- if (idx < 0) return null;
5697
- const pre = finding.Match.slice(0, idx);
5698
- const post = finding.Match.slice(idx + REDACTED_TOKEN.length);
5699
- if (span !== finding.Match && span.startsWith(pre) && span.endsWith(post) && span.length > pre.length + post.length) {
5700
- return {
5701
- value: span.slice(pre.length, span.length - post.length),
5702
- near: emptyToNull(lead + pre),
5703
- exact: true
5704
- };
5705
- }
5706
- return { value: null, near: emptyToNull(lead + pre), exact: false };
5811
+ return idx > 0 ? finding.Match.slice(0, idx) : null;
5812
+ }
5813
+ function siblingBounds(sibling, buf) {
5814
+ const aligned = alignFindingSpan(sibling, buf);
5815
+ if (aligned !== null) return { start: aligned.start, end: aligned.end };
5816
+ const reportedStart = sibling.StartColumn - 1;
5817
+ if (sibling.EndColumn <= reportedStart) return { start: 0, end: 0 };
5818
+ const start = Math.max(0, Math.min(reportedStart, buf.length) - SIBLING_PAD);
5819
+ return { start, end: Math.max(start, Math.min(sibling.EndColumn + SIBLING_PAD, buf.length)) };
5707
5820
  }
5708
5821
  function maskSiblingSpans(buf, siblings, ownStart, ownEnd) {
5709
5822
  if (siblings.length === 0) return buf;
5710
5823
  const copy = Buffer.from(buf);
5711
5824
  for (const sibling of siblings) {
5712
- const start = Math.max(1, Math.min(sibling.StartColumn, copy.length + 1)) - 1;
5713
- const end = Math.max(start, Math.min(sibling.EndColumn, copy.length));
5825
+ const { start, end } = siblingBounds(sibling, buf);
5714
5826
  const headEnd = Math.min(end, ownStart);
5715
5827
  if (start < headEnd) copy.fill(0, start, headEnd);
5716
5828
  const tailStart = Math.max(start, ownEnd);
@@ -5720,43 +5832,27 @@ function maskSiblingSpans(buf, siblings, ownStart, ownEnd) {
5720
5832
  }
5721
5833
  function resolveSecretContext(finding, readLine, siblings = []) {
5722
5834
  const resolved = resolveSpanContext(finding, readLine, siblings);
5723
- const ownStripped = withoutValue(resolved.near, resolved.value);
5724
- const multiLine = typeof finding.EndLine === "number" && finding.EndLine > finding.StartLine;
5725
5835
  return {
5726
5836
  value: resolved.value,
5727
- near: ownStripped,
5728
- exact: resolved.exact && !multiLine
5837
+ near: withoutValue(resolved.near, resolved.value),
5838
+ exact: resolved.exact
5729
5839
  };
5730
5840
  }
5841
+ function resolveWithoutLine(finding) {
5842
+ if (finding.Match.includes(REDACTED_TOKEN)) {
5843
+ return { value: null, near: templateLead(finding), exact: false };
5844
+ }
5845
+ return { value: emptyToNull(finding.Match), near: null, exact: false };
5846
+ }
5731
5847
  function resolveSpanContext(finding, readLine, siblings = []) {
5732
5848
  const raw = readLine(finding.File, finding.StartLine);
5733
- let span;
5734
- let lead;
5735
- if (raw !== null) {
5736
- const buf = Buffer.from(raw, "utf8");
5737
- const len = buf.length;
5738
- const startCol = Math.max(1, Math.min(finding.StartColumn, len + 1));
5739
- const endCol = Math.max(startCol, Math.min(finding.EndColumn, len));
5740
- span = buf.subarray(startCol - 1, endCol).toString("utf8");
5741
- lead = maskSiblingSpans(buf, siblings, startCol - 1, endCol).subarray(0, startCol - 1).toString("utf8");
5742
- } else {
5743
- span = finding.Match;
5744
- lead = "";
5745
- }
5746
- const aligned = alignRedactedMatch(finding, span, lead);
5747
- if (aligned !== null) return aligned;
5748
- if (typeof finding.Secret === "string" && finding.Secret.length > 0 && span.includes(finding.Secret)) {
5749
- return {
5750
- value: finding.Secret,
5751
- near: emptyToNull(lead + span.slice(0, span.indexOf(finding.Secret))),
5752
- exact: true
5753
- };
5754
- }
5755
- return {
5756
- value: span.length > 0 ? span : null,
5757
- near: emptyToNull(lead),
5758
- exact: false
5759
- };
5849
+ if (raw === null) return resolveWithoutLine(finding);
5850
+ const unresolved = { value: null, near: templateLead(finding), exact: false };
5851
+ const buf = Buffer.from(raw, "utf8");
5852
+ const aligned = alignFindingSpan(finding, buf);
5853
+ if (aligned === null) return unresolved;
5854
+ const near = maskSiblingSpans(buf, siblings, aligned.start, aligned.end).subarray(0, aligned.valueStart).toString("utf8");
5855
+ return { value: aligned.value, near: emptyToNull(near), exact: aligned.exact };
5760
5856
  }
5761
5857
  function formatNear(text) {
5762
5858
  const stripped = elideTokenRuns(text.replace(CONTROL_CHARS, ""));
@@ -9439,7 +9535,7 @@ function parseSyncArgs(argv) {
9439
9535
  // package.json
9440
9536
  var package_default = {
9441
9537
  name: "claude-nomad",
9442
- version: "0.63.0",
9538
+ version: "0.63.2",
9443
9539
  type: "module",
9444
9540
  description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
9445
9541
  keywords: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nomad",
3
- "version": "0.63.0",
3
+ "version": "0.63.2",
4
4
  "type": "module",
5
5
  "description": "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
6
6
  "keywords": [