@meridiona/meridian-darwin-arm64 1.31.3 → 1.32.1

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/.env.example CHANGED
@@ -68,18 +68,20 @@
68
68
  # PM_WORKLOG_SYNTH_TIMEOUT_S=300
69
69
 
70
70
  # ---------------------------------------------------------------------------
71
- # GitHub (GITHUB_TOKEN + GITHUB_ORG required to enable the GitHub connector)
72
- # Syncs the OPEN issues assigned to you under GITHUB_ORG; logs worklogs as
73
- # structured issue comments (GitHub has no native time tracking).
74
- # Token: Settings > Developer settings > Personal access tokens. A classic token
75
- # with the `repo` scope is simplest (works for personal + org issues); a
76
- # fine-grained token needs Issues: Read and write on the relevant repos.
77
- # GITHUB_ORG is the issue owner — an org slug OR your own username.
71
+ # GitHub (GITHUB_TOKEN required; GITHUB_PROJECT_IDS selects which Projects sync)
72
+ # Syncs the OPEN issues assigned to you from the listed GitHub Projects v2 (read
73
+ # via the GraphQL API); logs worklogs as structured issue comments (GitHub has no
74
+ # native time tracking).
75
+ # Token: easiest is the gh CLI `meridian setup` runs `gh auth token` and adds
76
+ # the read:project scope for you (no PAT). Otherwise create a classic PAT with
77
+ # the `repo`, `read:org`, `read:project` scopes (read:project reads Projects;
78
+ # repo posts worklog comments).
79
+ # GITHUB_PROJECT_IDS: comma-separated Projects v2 node IDs (PVT_xxx). Find them:
80
+ # gh api graphql -f query='{ viewer { projectsV2(first:10){nodes{id title}} } }'
78
81
  # ---------------------------------------------------------------------------
79
82
 
80
83
  # GITHUB_TOKEN=ghp_your_personal_access_token
81
- # GITHUB_ORG=your-org
82
- # GITHUB_REPOS=your-org/api,your-org/web # optional — comma-separated owner/repo; empty = all repos under the owner
84
+ # GITHUB_PROJECT_IDS=PVT_xxx,PVT_yyy
83
85
 
84
86
  # ---------------------------------------------------------------------------
85
87
  # Linear (LINEAR_API_KEY required to enable the Linear connector)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.31.3
1
+ 1.32.1
package/bin/meridian CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridiona/meridian-darwin-arm64",
3
- "version": "1.31.3",
3
+ "version": "1.32.1",
4
4
  "description": "Prebuilt Meridian app for macOS arm64 (daemon binary + dashboard + Python services). Installed via @meridiona/meridian.",
5
5
  "homepage": "https://github.com/Meridiona/meridian",
6
6
  "repository": {
@@ -3,8 +3,8 @@
3
3
  #
4
4
  # Install a PREBUILT release bundle (no cargo/npm build). Run from inside an
5
5
  # unpacked bundle at ~/.meridian/app — bootstrap.sh downloads + unpacks the
6
- # release tarball and execs this. Installs prerequisites, the Python venv + MLX
7
- # deps, and registers the four launchd daemons pointing at this bundle.
6
+ # release tarball and execs this. Installs prerequisites, builds the Python venv
7
+ # from PyPI via uv sync, and registers the four launchd daemons pointing at this bundle.
8
8
  #
9
9
  # bash ~/.meridian/app/scripts/install-from-bundle.sh [--skip-permissions]
10
10
  set -euo pipefail
@@ -94,9 +94,16 @@ collect_credentials() {
94
94
  fi
95
95
  echo >&2
96
96
  if prompt_category "GitHub"; then
97
- prompt_env_var "GITHUB_TOKEN" "GitHub personal access token" 1 "$env_file"
98
- prompt_env_var "GITHUB_ORG" "GitHub organization (or your username)" 0 "$env_file"
99
- prompt_env_var "GITHUB_REPOS" "GitHub repos (optional, comma-sep owner/repo)" 0 "$env_file"
97
+ if ! _try_gh_token "$env_file"; then
98
+ echo >&2
99
+ echo " Alternatively, create a personal access token (classic) at:" >&2
100
+ echo " https://github.com/settings/tokens/new" >&2
101
+ echo " Required scopes: repo, read:org, read:project" >&2
102
+ echo " (read:project lets meridian read your GitHub Projects; repo posts worklog comments)" >&2
103
+ echo >&2
104
+ prompt_env_var "GITHUB_TOKEN" "GitHub personal access token" 1 "$env_file"
105
+ fi
106
+ _pick_github_projects "$env_file"
100
107
  fi
101
108
  echo >&2
102
109
  if prompt_category "Linear"; then
@@ -106,6 +113,9 @@ collect_credentials() {
106
113
  ok "Credential collection complete"
107
114
  }
108
115
 
116
+ # GitHub setup helpers — shared with install.sh.
117
+ source "${APP_ROOT}/scripts/lib-github-setup.sh"
118
+
109
119
  GUI_TARGET="gui/$(id -u)"
110
120
  LAUNCH_AGENTS="${HOME}/Library/LaunchAgents"
111
121
 
@@ -327,6 +337,24 @@ if ! command -v screenpipe >/dev/null 2>&1; then
327
337
  fi
328
338
  fi
329
339
  ok "screenpipe"
340
+ # Stage the real screenpipe Mach-O to ~/.meridian/bin/screenpipe — a stable path
341
+ # that is independent of the npm prefix (nvm users get a version-specific path
342
+ # under ~/.nvm that breaks on `nvm use` and is too deep to navigate in System
343
+ # Settings). The launchd plist and TCC grants are written against this path.
344
+ mkdir -p "${HOME}/.meridian/bin"
345
+ _sp_npm_root="$(npm root -g 2>/dev/null || true)"
346
+ _sp_real=""
347
+ if [[ -n "${_sp_npm_root}" && -d "${_sp_npm_root}/screenpipe" ]]; then
348
+ while IFS= read -r _sp_cand; do
349
+ if file "${_sp_cand}" 2>/dev/null | grep -q "Mach-O"; then _sp_real="${_sp_cand}"; break; fi
350
+ done < <(find "${_sp_npm_root}/screenpipe" -type f -name screenpipe -perm +0111 2>/dev/null)
351
+ fi
352
+ if [[ -n "${_sp_real}" ]]; then
353
+ cmp -s "${_sp_real}" "${HOME}/.meridian/bin/screenpipe" 2>/dev/null \
354
+ || cp "${_sp_real}" "${HOME}/.meridian/bin/screenpipe"
355
+ chmod +x "${HOME}/.meridian/bin/screenpipe"
356
+ ok "screenpipe staged → ${HOME}/.meridian/bin/screenpipe"
357
+ fi
330
358
  if ! command -v ffmpeg >/dev/null 2>&1; then info "Installing ffmpeg…"; brew install ffmpeg; fi
331
359
  ok "ffmpeg"
332
360
 
@@ -408,115 +436,39 @@ _mlx_was_healthy=0
408
436
  curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1 && _mlx_was_healthy=1
409
437
 
410
438
  # ── 4. Python venv + MLX deps ────────────────────────────────────────────────
411
- # The venv (~160 MB) is too large for the npm registry, so release builds attach
412
- # it to the GitHub Release and we download it here. Three paths:
413
- # A) Already current: the installed venv's stamp matches the shipped uv.lock
414
- # hash skip the 160 MB download entirely (differential update). uv.lock is
415
- # the deterministic source of truth for the deps and travels in the npm
416
- # package, so this makes most `meridian update` runs touch zero network.
417
- # B) Deps changed / fresh install: download services-venv-<ver>.tar.gz, verify
418
- # it against the remote .sha256, extract site-packages into a fresh Python
419
- # 3.11 venv. No PyPI. Stamp the uv.lock hash for next time.
420
- # C) Dev/source install (no VERSION, or download/verify failed): `uv sync
421
- # --frozen` from uv.lock — downloads from PyPI the first time (~40s).
422
- # NOTE the differential key is the uv.lock hash, NOT the tarball hash: BSD tar
423
- # embeds mtimes so the tarball hash differs on every CI build even when deps are
424
- # identical, which would defeat the skip. The remote .sha256 is used only to
425
- # verify download integrity, never for the change decision.
439
+ # Installed from PyPI via uv sync at install time no pre-built venv shipped.
440
+ # PyPI MLX wheels are tagged macosx_13_0_arm64 / macosx_14_0_arm64 so pip's
441
+ # platform tag filter guarantees the correct ABI for the user's macOS version.
442
+ # Differential skip: if uv.lock hasn't changed and mlx.core imports cleanly,
443
+ # the venv is already correct no network round-trip needed.
426
444
  VENV="${APP_ROOT}/services/.venv"
427
445
  VENV_STAMP="${VENV}/.meridian-venv-hash"
428
- _VERSION="$(cat "${APP_ROOT}/VERSION" 2>/dev/null | tr -d '[:space:]' || true)"
429
- _VENV_URL="https://github.com/Meridiona/meridian/releases/download/v${_VERSION}/services-venv-${_VERSION}.tar.gz"
430
446
  _LOCK_HASH="$(shasum -a 256 "${APP_ROOT}/services/uv.lock" 2>/dev/null | cut -d' ' -f1 || true)"
431
447
 
432
- # Extract a downloaded venv tarball ($1) into a fresh Python 3.11 venv, then stamp
433
- # the dependency hash ($2). The tarball is compiled for Python 3.11 (package-release.sh
434
- # enforces this); the venv MUST use exactly 3.11 or the cpython-311 .so files fail
435
- # to import. Prefer system python3.11, then uv-managed 3.11, then install it via uv.
436
- _extract_venv() {
437
- local tgz="$1" stamp_hash="$2" tarball_python="" py_dir="" venv_tmp=""
438
- # Stage the new venv to a temp path; swap atomically only on success so a
439
- # failed extraction (disk full, corrupt archive) never destroys the live venv.
440
- venv_tmp="${VENV}.tmp.$$"
441
- rm -rf "${venv_tmp}"
442
- if command -v python3.11 >/dev/null 2>&1; then
443
- tarball_python="$(command -v python3.11)"
444
- elif "${UV_BIN}" python find 3.11 >/dev/null 2>&1; then
445
- tarball_python="$("${UV_BIN}" python find 3.11)"
446
- else
447
- info "Installing Python 3.11 (pre-built venv requires it — one-time download)…"
448
- "${UV_BIN}" python install 3.11
449
- tarball_python="$("${UV_BIN}" python find 3.11)"
450
- fi
451
- "${UV_BIN}" venv --python "${tarball_python}" "${venv_tmp}" 2>/dev/null
452
- py_dir="$(ls "${venv_tmp}/lib/" | grep '^python' | head -1)"
453
- mkdir -p "${venv_tmp}/lib/${py_dir}/site-packages"
454
- tar -xzf "${tgz}" -C "${venv_tmp}/lib/${py_dir}/site-packages"
455
- # Install the local editable package (meridian-agents) — no deps needed,
456
- # everything is already in site-packages from the tarball.
457
- "${UV_BIN}" pip install --quiet --no-deps --python "${venv_tmp}/bin/python" -e "${APP_ROOT}/services"
458
- # All steps succeeded — atomically replace the live venv.
459
- rm -rf "${VENV}"
460
- mv "${venv_tmp}" "${VENV}"
461
- printf '%s\n' "${stamp_hash}" > "${VENV_STAMP}"
462
- ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
463
- }
448
+ _venv_changed=1
449
+ _have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
464
450
 
465
- # uv sync fallback (Path C) used for dev/source installs and as the offline
466
- # failure path. Both extras: mlx (classifier + server) AND pm_worklog_update
467
- # (agno) the one MLX server serves /classify_sessions AND /synthesise_worklog,
468
- # so without agno worklog synthesis 500s with ModuleNotFoundError. Stamp the
469
- # uv.lock hash on success so subsequent runs can skip.
470
- _venv_uv_sync() {
471
- info "Building Python venv from uv.lock (mlx-lm/outlines/fastapi/agno; first run may download a few hundred MB)…"
451
+ if [[ -n "${_LOCK_HASH}" && "${_LOCK_HASH}" == "${_have_hash}" && -x "${VENV}/bin/python" ]] \
452
+ && "${VENV}/bin/python" -c "import mlx.core" 2>/dev/null; then
453
+ ok "Python deps unchanged skipping uv sync"
454
+ _venv_changed=0
455
+ else
456
+ info "Installing Python services from PyPI (mlx-lm/outlines/fastapi/agno; first run ~40–120s)…"
472
457
  if "${UV_BIN}" sync \
473
458
  --project "${APP_ROOT}/services" \
474
459
  --extra mlx \
475
460
  --extra pm_worklog_update \
476
461
  --frozen \
477
- --python "${PYTHON_BIN}"; then
462
+ --python 3.11; then
478
463
  [[ -n "${_LOCK_HASH}" ]] && printf '%s\n' "${_LOCK_HASH}" > "${VENV_STAMP}"
479
464
  ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
480
465
  else
481
466
  warn "uv sync failed — leaving venv as-is; re-run 'meridian setup' to retry"
482
467
  fi
483
- }
484
-
485
- _venv_changed=1
486
- _have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
487
-
488
- if [[ -n "${_LOCK_HASH}" && "${_LOCK_HASH}" == "${_have_hash}" && -x "${VENV}/bin/python" ]]; then
489
- # Path A — deps unchanged since this venv was built. No network, no rebuild.
490
- ok "Python deps unchanged — reusing existing venv (skipped 160 MB download)"
491
- _venv_changed=0
492
- elif [[ -n "${_VERSION}" ]]; then
493
- # Path B — release install/update: fetch the integrity hash, then the tarball.
494
- _remote_sha="$(curl -fsSL --retry 3 "${_VENV_URL}.sha256" 2>/dev/null | tr -d '[:space:]' || true)"
495
- if [[ -n "${_remote_sha}" ]]; then
496
- info "Downloading pre-built Python venv (~160 MB, one-time per dependency change)…"
497
- _venv_tmp="$(mktemp -d)"; _venv_tgz="${_venv_tmp}/services-venv.tar.gz"
498
- if curl -fsSL --retry 3 "${_VENV_URL}" -o "${_venv_tgz}" \
499
- && [[ "$(shasum -a 256 "${_venv_tgz}" | cut -d' ' -f1)" == "${_remote_sha}" ]]; then
500
- # Stamp the uv.lock hash (deterministic) so future updates can skip.
501
- _extract_venv "${_venv_tgz}" "${_LOCK_HASH:-${_remote_sha}}"
502
- rm -rf "${_venv_tmp}"
503
- else
504
- warn "venv download or SHA-256 verification failed — falling back to uv sync"
505
- rm -rf "${_venv_tmp}"
506
- _venv_uv_sync
507
- fi
508
- else
509
- warn "venv release asset unreachable for v${_VERSION} — falling back to uv sync"
510
- _venv_uv_sync
511
- fi
512
- else
513
- # Path C — dev/source install (no VERSION stamp).
514
- _venv_uv_sync
515
468
  fi
516
469
 
517
470
  # On macOS 26+, install apple-fm-sdk so Apple Intelligence is used instead of
518
- # downloading a large MLX model. This runs after both venv paths (tarball or uv
519
- # sync) so the package is available regardless of how the venv was built.
471
+ # downloading a large MLX model. Runs after uv sync so the venv exists.
520
472
  # apple-fm-sdk only installs on macOS 26+ (links against system frameworks);
521
473
  # on older macOS pip will fail gracefully and MLX is used as the fallback.
522
474
  _macos_major="$(sw_vers -productVersion 2>/dev/null | cut -d. -f1)"
@@ -545,13 +497,19 @@ fi
545
497
  if [[ "${SKIP_PERMISSIONS}" -eq 0 ]]; then
546
498
  echo "→ screenpipe needs 2 macOS permissions: Screen Recording and Accessibility."
547
499
  echo " (Audio capture is disabled, so no Microphone permission is required.)"
500
+ echo " You will add these 2 binaries — both live at the same stable path:"
501
+ echo " ${HOME}/.meridian/bin/screenpipe ← Screen Recording + Accessibility"
502
+ echo " ${HOME}/.meridian/bin/meridian-a11y-helper ← Accessibility only"
503
+ echo " In each pane: click '+' → press ⌘⇧G → paste the path above → Open → toggle ON."
548
504
  read -r -p " Press Enter to open Screen Recording settings… " _ || true
549
505
  open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" 2>/dev/null || true
506
+ echo " Add: ${HOME}/.meridian/bin/screenpipe"
550
507
  read -r -p " Press Enter to open Accessibility settings… " _ || true
551
508
  open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null || true
552
- echo " In the SAME Accessibility pane, also add the a11y helper (+ → ⌘⇧G → paste):"
509
+ echo " Add both:"
510
+ echo " ${HOME}/.meridian/bin/screenpipe"
553
511
  echo " ${HOME}/.meridian/bin/meridian-a11y-helper"
554
- echo " Without it, Electron apps (Claude, Codex, Slack, …) stay invisible to capture."
512
+ echo " Without the a11y helper, Electron apps (Claude, Codex, Slack, …) stay invisible to capture."
555
513
  read -r -p " Press Enter once all are granted… " _ || true
556
514
  fi
557
515
 
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # meridian — normalises screenpipe activity into structured app sessions
3
+ #
4
+ # Shared GitHub setup helpers, sourced by both install.sh (source installs) and
5
+ # scripts/install-from-bundle.sh (bundle installs). The sourcing script must
6
+ # already define: info, ok, warn, get_env_value, set_env_value (resolved at
7
+ # call time, so definition order across files does not matter).
8
+
9
+ # Obtain a GitHub token from the gh CLI — no PAT needed. meridian needs two
10
+ # scopes: `repo` (post worklog / task-update issue comments) and `read:project`
11
+ # (read Projects v2 via GraphQL). gh's default web-login grants repo + read:org
12
+ # but not read:project, so add whatever is missing through the same browser flow,
13
+ # then write the OAuth token to GITHUB_TOKEN. Returns non-zero if gh is missing,
14
+ # unauthenticated, or the scope refresh fails, so the caller can fall back to a
15
+ # manual PAT prompt. An existing GITHUB_TOKEN is kept untouched.
16
+ _try_gh_token() {
17
+ local env_file="$1"
18
+ [[ -n "$(get_env_value GITHUB_TOKEN "$env_file")" ]] && {
19
+ ok "GITHUB_TOKEN already set — keeping"; return 0
20
+ }
21
+ command -v gh >/dev/null 2>&1 || return 1
22
+ gh auth status >/dev/null 2>&1 || return 1
23
+
24
+ # Add any missing scope through gh's browser flow. `project` (write) satisfies
25
+ # the read:project requirement too, so accept either.
26
+ local status; status="$(gh auth status 2>&1)"
27
+ local want=()
28
+ grep -q "'repo'" <<< "$status" || want+=("repo")
29
+ grep -qE "'read:project'|'project'" <<< "$status" || want+=("read:project")
30
+ if (( ${#want[@]} > 0 )); then
31
+ local joined; printf -v joined '%s,' "${want[@]}"; joined="${joined%,}"
32
+ info " Granting the ${joined} scope(s) to your gh login (opens a browser)…"
33
+ gh auth refresh -h github.com -s "$joined" >&2 || {
34
+ warn " Could not extend gh scopes — use a personal access token instead"
35
+ return 1
36
+ }
37
+ fi
38
+
39
+ local token
40
+ token="$(gh auth token 2>/dev/null)" || return 1
41
+ [[ -z "$token" ]] && return 1
42
+ set_env_value GITHUB_TOKEN "$token" "$env_file"
43
+ ok "GITHUB_TOKEN set from gh CLI (no PAT needed)"
44
+ }
45
+
46
+ # Interactively pick GitHub Projects and write their node IDs to GITHUB_PROJECT_IDS.
47
+ # Lists both personal and org projects via GraphQL. No-op if already set or if
48
+ # the gh CLI is unavailable or unauthenticated.
49
+ _pick_github_projects() {
50
+ local env_file="$1"
51
+ [[ -n "$(get_env_value GITHUB_PROJECT_IDS "$env_file")" ]] && {
52
+ ok "GITHUB_PROJECT_IDS already set — keeping"; return 0
53
+ }
54
+ command -v gh >/dev/null 2>&1 || return 0
55
+ gh auth status >/dev/null 2>&1 || return 0
56
+
57
+ local raw
58
+ raw="$(gh api graphql -f query='
59
+ { viewer {
60
+ projectsV2(first: 20) { nodes { id title } }
61
+ organizations(first: 20) {
62
+ nodes { login projectsV2(first: 20) { nodes { id title } } }
63
+ }
64
+ } }' 2>/dev/null)" || {
65
+ warn "Could not list GitHub Projects — add GITHUB_PROJECT_IDS to the config manually if needed"
66
+ return 0
67
+ }
68
+
69
+ # One python3 pass emits "id<TAB>label" per project (personal + org). python3
70
+ # is always present on macOS.
71
+ local pairs_raw
72
+ pairs_raw="$(printf '%s' "$raw" | python3 -c "
73
+ import json, sys
74
+ d = json.load(sys.stdin).get('data', {}).get('viewer', {})
75
+ for n in d.get('projectsV2', {}).get('nodes', []):
76
+ print('%s\t%s' % (n['id'], n['title']))
77
+ for org in d.get('organizations', {}).get('nodes', []):
78
+ for n in org.get('projectsV2', {}).get('nodes', []):
79
+ print('%s\t%s / %s' % (n['id'], org['login'], n['title']))
80
+ " 2>/dev/null)" || true
81
+
82
+ # Split each "id<TAB>label" line into parallel arrays (bash 3.2 — no mapfile).
83
+ local _ids=() _labels=()
84
+ local _id _label
85
+ while IFS=$'\t' read -r _id _label; do
86
+ [[ -z "$_id" ]] && continue
87
+ _ids+=("$_id"); _labels+=("$_label")
88
+ done <<< "$pairs_raw"
89
+ local count=${#_ids[@]}
90
+ (( count == 0 )) && { warn "No GitHub Projects found for your account"; return 0; }
91
+
92
+ echo >&2
93
+ echo " Your GitHub Projects:" >&2
94
+ local i=0
95
+ while (( i < count )); do
96
+ printf " %d. %s\n" "$((i+1))" "${_labels[$i]}" >&2
97
+ i=$((i+1))
98
+ done
99
+ echo >&2
100
+
101
+ local selection
102
+ read -r -p " Enter project numbers (comma-sep, e.g. 1,2) or Enter to skip: " selection
103
+ [[ -z "$selection" ]] && { info " (skipped GITHUB_PROJECT_IDS)"; return 0; }
104
+
105
+ local selected_ids=()
106
+ local IFS_save="$IFS"
107
+ IFS=',' read -ra nums <<< "$selection"
108
+ IFS="$IFS_save"
109
+ local n
110
+ for n in "${nums[@]}"; do
111
+ n="${n//[[:space:]]/}"
112
+ if [[ "$n" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= count )); then
113
+ selected_ids+=("${_ids[$((n-1))]}")
114
+ fi
115
+ done
116
+
117
+ if [[ ${#selected_ids[@]} -eq 0 ]]; then
118
+ info " (no valid selection — skipped GITHUB_PROJECT_IDS)"; return 0
119
+ fi
120
+
121
+ local joined
122
+ printf -v joined '%s,' "${selected_ids[@]}"
123
+ set_env_value GITHUB_PROJECT_IDS "${joined%,}" "$env_file"
124
+ ok "GITHUB_PROJECT_IDS set (${#selected_ids[@]} project(s))"
125
+ }
@@ -20,12 +20,11 @@ mkdir -p "$(dirname "${APP}")"
20
20
  keep=""
21
21
  if [[ -f "${APP}/.env" ]]; then keep="$(mktemp)"; cp "${APP}/.env" "${keep}"; fi
22
22
 
23
- # Preserve the Python venv across updates. Rebuilding it (python -m venv + pip
24
- # install mlx-lm/outlines/…) costs minutes; most releases don't change Python
25
- # deps, so move it aside and restore it to the SAME absolute path (its baked-in
26
- # shebangs stay valid). install-from-bundle.sh then only re-pips when the deps
27
- # hash actually changes. Kept in a sibling dir under ~/.meridian so the move is
28
- # an instant rename (same filesystem), never a cross-volume copy.
23
+ # Preserve the Python venv across updates. The venv is built from PyPI via
24
+ # uv sync at install time; preserving it means install-from-bundle.sh only
25
+ # re-syncs when uv.lock actually changed. Kept in a sibling dir under
26
+ # ~/.meridian so the move is an instant rename (same filesystem), never a
27
+ # cross-volume copy.
29
28
  venv_keep="${HOME}/.meridian/.venv-update-keep"
30
29
  rm -rf "${venv_keep}"
31
30
  if [[ -d "${APP}/services/.venv" ]]; then mv "${APP}/services/.venv" "${venv_keep}"; fi
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meridian-agents"
7
- version = "1.31.3"
7
+ version = "1.32.1"
8
8
  description = "Meridian agents — hermes task linking and Jira progress updates for meridian.db"
9
9
  requires-python = ">=3.11"
10
10
  authors = [{ name = "Meridiona" }]
package/ui.tar.gz CHANGED
Binary file