synabun 2026.4.26-beta.13 → 2026.4.26-beta.15

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.
@@ -10604,7 +10604,10 @@ async function checkSynabunUpdate() {
10604
10604
  } catch { /* keep prior current if package.json read fails */ }
10605
10605
 
10606
10606
  const curParsed = parseSemver(current);
10607
- const installedChannel = curParsed?.pre ? 'prerelease' : 'stable';
10607
+ // Numeric-only suffix (`2026.4.26-1`) is a post-release iteration, not a
10608
+ // prerelease — keep those on the stable channel. Only true alpha/beta/rc
10609
+ // suffixes flip the channel.
10610
+ const installedChannel = (curParsed?.pre && curParsed.iter == null) ? 'prerelease' : 'stable';
10608
10611
 
10609
10612
  const fetchJsonWithTimeout = async (url, ms = 5000) => {
10610
10613
  const controller = new AbortController();
@@ -10799,25 +10802,36 @@ function parseVersion(raw) {
10799
10802
  }
10800
10803
 
10801
10804
  // Structured semver parse — handles calver bumps and prerelease tags:
10802
- // "2026.4.26" -> { major:2026, minor:4, patch:26, pre:null }
10803
- // "2026.4.20-2" -> { major:2026, minor:4, patch:20, pre:"2" }
10804
- // "2026.4.26-beta.3" -> { major:2026, minor:4, patch:26, pre:"beta.3" }
10805
- // "v1.2.3-rc.1" -> { major:1, minor:2, patch:3, pre:"rc.1" }
10805
+ // "2026.4.26" -> { major:2026, minor:4, patch:26, pre:null, iter:null }
10806
+ // "2026.4.20-2" -> { major:2026, minor:4, patch:20, pre:"2", iter:2 }
10807
+ // "2026.4.26-beta.3" -> { major:2026, minor:4, patch:26, pre:"beta.3", iter:null }
10808
+ // "v1.2.3-rc.1" -> { major:1, minor:2, patch:3, pre:"rc.1", iter:null }
10809
+ //
10810
+ // `iter` is set when the suffix is purely numeric (SynaBun's calver iteration
10811
+ // scheme: `2026.4.26-1` is the FIRST patch published after `2026.4.26`).
10812
+ // Strict semver §9 treats any prerelease suffix as PRE-release (lower than no
10813
+ // suffix). SynaBun publishes calver iterations as `latest` on npm, so we need
10814
+ // to treat numeric-only suffixes as POST-release iterations instead.
10806
10815
  function parseSemver(raw) {
10807
10816
  if (raw == null) return null;
10808
10817
  const s = String(raw).trim().replace(/^v\.?/, '');
10809
10818
  const m = s.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/);
10810
10819
  if (!m) return null;
10820
+ const pre = m[4] || null;
10821
+ const iter = pre && /^\d+$/.test(pre) ? Number(pre) : null;
10811
10822
  return {
10812
10823
  major: +m[1],
10813
10824
  minor: +m[2],
10814
10825
  patch: +m[3],
10815
- pre: m[4] || null,
10826
+ pre,
10827
+ iter,
10816
10828
  };
10817
10829
  }
10818
10830
 
10819
- // Semver-spec precedence including prerelease handling:
10820
- // 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0
10831
+ // Precedence (mix of semver §11 + SynaBun calver-iteration convention):
10832
+ // 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0 < 1.0.0-1 < 1.0.0-2
10833
+ // Numeric-only suffix = post-release iteration (NEWER than no-suffix release).
10834
+ // Mixed/alpha suffix = prerelease (OLDER than no-suffix release, per semver).
10821
10835
  // Returns negative if a<b, positive if a>b, 0 if equal. Accepts strings or
10822
10836
  // parsed objects.
10823
10837
  function compareSemver(a, b) {
@@ -10829,11 +10843,19 @@ function compareSemver(a, b) {
10829
10843
  if (pa.major !== pb.major) return pa.major - pb.major;
10830
10844
  if (pa.minor !== pb.minor) return pa.minor - pb.minor;
10831
10845
  if (pa.patch !== pb.patch) return pa.patch - pb.patch;
10832
- // Equal x.y.z. Prerelease vs none: a release without prerelease is GREATER.
10846
+ // Equal x.y.z. Order from oldest → newest:
10847
+ // alpha-prerelease < no-suffix release < numeric iteration
10833
10848
  if (!pa.pre && !pb.pre) return 0;
10849
+ // Numeric iterations are POST-release: always newer than no-suffix and
10850
+ // newer than alpha prereleases.
10851
+ if (pa.iter != null && pb.iter == null) return 1;
10852
+ if (pa.iter == null && pb.iter != null) return -1;
10853
+ if (pa.iter != null && pb.iter != null) return pa.iter - pb.iter;
10854
+ // Neither side is a numeric iteration. Standard semver §11: no-suffix is
10855
+ // greater than alpha prerelease.
10834
10856
  if (!pa.pre && pb.pre) return 1;
10835
10857
  if ( pa.pre && !pb.pre) return -1;
10836
- // Both have prerelease — compare identifiers per semver §11.4.
10858
+ // Both have alpha-flavoured prerelease — compare identifiers per semver §11.4.
10837
10859
  const ia = pa.pre.split('.');
10838
10860
  const ib = pb.pre.split('.');
10839
10861
  const len = Math.max(ia.length, ib.length);
@@ -10886,6 +10908,7 @@ function detectInstallSource(binPath) {
10886
10908
  if (/\/node_modules\//.test(p) || /\/npm\/node_modules\//.test(p)) return 'npm';
10887
10909
  if (p.includes('/opt/homebrew/') || p.includes('/usr/local/cellar/') || /\/homebrew\//.test(p)) return 'brew';
10888
10910
  if (p.includes('/.claude/local/') || p.includes('/anthropicclaude/')) return 'native';
10911
+ if (/\/\.local\/bin\//.test(p)) return 'native';
10889
10912
  if (/\/program files( \(x86\))?\//.test(p) || /\/localappdata\/.*anthropicclaude/.test(p)) return 'native';
10890
10913
  if (p.includes('/winget/') || p.includes('/microsoft/winget/')) return 'winget';
10891
10914
  if (/\/usr\/local\/lib\/node_modules\//.test(p)) return 'npm';
@@ -11014,15 +11037,19 @@ async function checkToolVersions() {
11014
11037
  }
11015
11038
 
11016
11039
  // Nuclear safety net for the recurring claude phantom-version bug:
11017
- // if the probe returned a known historical bundled version of claude-code,
11018
- // reject it even if the binary-path detection failed. A real user-global
11019
- // install of claude is always current (2.1.116+), so seeing 2.1.89 etc.
11020
- // means we're reading a stale bundle SynaBun shipped in a prior release.
11021
- if (tool.key === 'claude-code' && installed && KNOWN_STALE_CLAUDE_VERSIONS.has(installed)) {
11040
+ // only reject when binary path detection failed (truly phantom). If we
11041
+ // resolved a path AND it's outside SynaBun's own dirs, trust the version
11042
+ // — Anthropic ships these versions in the native installer too, so a
11043
+ // version-only blacklist hits legit installs at ~/.local/bin/claude.exe.
11044
+ if (
11045
+ tool.key === 'claude-code' &&
11046
+ installed &&
11047
+ KNOWN_STALE_CLAUDE_VERSIONS.has(installed) &&
11048
+ !probedBinPath
11049
+ ) {
11022
11050
  console.warn(
11023
- ` [version-guard] Rejected claude v${installed} from ${probedBinPath || '(unknown path)'}` +
11024
- ` — matches known-stale SynaBun bundle. Treating as not installed. ` +
11025
- `If you actually have this version installed, remove it from KNOWN_STALE_CLAUDE_VERSIONS.`
11051
+ ` [version-guard] Rejected claude v${installed} from (unknown path)` +
11052
+ ` — matches known-stale SynaBun bundle and no binary path could be resolved.`
11026
11053
  );
11027
11054
  installed = null;
11028
11055
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synabun",
3
- "version": "2026.4.26-beta.13",
3
+ "version": "2026.4.26-beta.15",
4
4
  "description": "Persistent vector memory for AI assistants",
5
5
  "license": "Apache-2.0",
6
6
  "author": "The SynaBun Authors",