@massa-ai/claude-plugin 1.45.0 → 1.47.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "massa-ai",
3
- "version": "1.45.0",
3
+ "version": "1.47.0",
4
4
  "description": "massa-ai — semantic code search, durable memory, symbol graph, and context compression",
5
5
  "author": {
6
6
  "name": "Luiz Massa",
package/install.sh CHANGED
@@ -44,6 +44,11 @@ source "$REPO_ROOT/scripts/lib/installer-shared.sh"
44
44
  SCOPE="user"
45
45
  UNINSTALL=0
46
46
  DRY_RUN=0
47
+ # Served-plugin version on the marketplace route, filled by
48
+ # refresh_marketplace_cache and read by record_plugin_version. Declared at
49
+ # top level so routes that never refresh (file route, uninstall) cannot trip
50
+ # `set -u` (CMR R3).
51
+ PLUGIN_SERVED_VERSION=""
47
52
  # The marketplace root to register. install-harness.sh resolves this once for
48
53
  # the whole run (--plugin-source local|copy|auto); a direct invocation of this
49
54
  # script defaults to the checkout it lives in.
@@ -426,7 +431,6 @@ record_plugin_version() {
426
431
  fi
427
432
 
428
433
  local version installed_at route
429
- version="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$SCRIPT_DIR/package.json" | head -n 1)"
430
434
  installed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
431
435
  # installRoute (T8, design F1): installer-owned, engine-read-only. The
432
436
  # marketplace route (register_claude_plugin succeeded) serves agents in
@@ -434,6 +438,17 @@ record_plugin_version() {
434
438
  # Written on EVERY install path — an absent field is what makes the switch
435
439
  # engine refuse loud rather than guess (hosts.ts detectRoute).
436
440
  if [[ "$PLUGIN_ROUTE" -eq 1 ]]; then route="marketplace"; else route="file"; fi
441
+ # Version claim (CMR-02/03): the file route records the bundle version — it
442
+ # IS what was just copied. The marketplace route records what the CLI
443
+ # actually serves (refresh_marketplace_cache), which the version-pinned
444
+ # cache can hold BEHIND the bundle; recording the bundle version there is
445
+ # the lie that froze this host's harness gate at a stale cache. Empty
446
+ # served version → record no version at all (next run retries).
447
+ if [[ "$PLUGIN_ROUTE" -eq 1 ]]; then
448
+ version="$PLUGIN_SERVED_VERSION"
449
+ else
450
+ version="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$SCRIPT_DIR/package.json" | head -n 1)"
451
+ fi
437
452
 
438
453
  # Tolerant of a corrupt/missing state file (rewrites a minimal valid one —
439
454
  # AC-8). A record-write failure warns but never fails the install: the next
@@ -458,7 +473,13 @@ const rec =
458
473
  data.platforms[host] && typeof data.platforms[host] === "object" && !Array.isArray(data.platforms[host])
459
474
  ? data.platforms[host]
460
475
  : { root, skillsOwner: "plugin", skills: [] };
461
- rec.plugin = { version, installedAt };
476
+ // An empty version means the served version was unreadable (CMR-03): keep
477
+ // any prior plugin record untouched and claim nothing new — an absent/stale
478
+ // record is what makes the next harness run retry. installRoute is still
479
+ // written below on every path.
480
+ if (version) {
481
+ rec.plugin = { version, installedAt };
482
+ }
462
483
  // installRoute is installer-owned (like plugin above); modelProfile is NEVER
463
484
  // written here — the switch engine (packages/shared/src/profile-switch/) is
464
485
  // its sole writer, and this installer only ever reads it (see
@@ -507,6 +528,89 @@ try {
507
528
  NODE
508
529
  }
509
530
 
531
+ # ── Marketplace install-path resolution (T19, CPP-07) ───────────────────────
532
+ # Mirrors packages/shared/src/profile-switch/claude-marketplace.ts's
533
+ # resolveClaudeMarketplaceRoot exactly (same selection rule: scope:"user"
534
+ # preferred, then most recent lastUpdated, then last entry in array order;
535
+ # require the resolved installPath to exist on disk) so the shell installer
536
+ # and the TS switch engine agree on which cache directory is "current".
537
+ # Never caches — the path is version pinned and moves on every
538
+ # `claude plugin update`. Prints the resolved installPath, or "" when
539
+ # unresolvable (absent/corrupt registry, no record, or a path that doesn't
540
+ # exist on disk). Never throws.
541
+ resolve_claude_install_path() {
542
+ local runner="$1"
543
+ "$runner" - "$PLUGIN_REGISTRY" "$PLUGIN_ID" <<'NODE'
544
+ const fs = require("fs");
545
+ const [, , file, id] = process.argv;
546
+ try {
547
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
548
+ const records = data && data.plugins ? data.plugins[id] : null;
549
+ if (!Array.isArray(records) || records.length === 0) process.exit(0);
550
+ const userScoped = records.filter((r) => r && r.scope === "user");
551
+ const pool = userScoped.length > 0 ? userScoped : records;
552
+ let best;
553
+ let bestTime = -Infinity;
554
+ for (const record of pool) {
555
+ const parsed = record && record.lastUpdated ? Date.parse(record.lastUpdated) : NaN;
556
+ if (Number.isFinite(parsed) && parsed >= bestTime) {
557
+ best = record;
558
+ bestTime = parsed;
559
+ }
560
+ }
561
+ const selected = best || pool[pool.length - 1];
562
+ const installPath = selected && selected.installPath;
563
+ if (!installPath) process.exit(0);
564
+ if (!fs.existsSync(installPath)) process.exit(0);
565
+ process.stdout.write(installPath);
566
+ } catch { /* unresolvable — absent file, unreadable file, unparseable JSON */ }
567
+ NODE
568
+ }
569
+
570
+ # Re-applies the recorded model profile to the marketplace install root after
571
+ # a `claude plugin update` succeeds (CPP-07). AD-015 (read-only): this
572
+ # function, like recorded_profile above, NEVER writes
573
+ # platforms.claude.modelProfile — the switch engine
574
+ # (packages/shared/src/profile-switch/) is its sole writer. An update moves
575
+ # installPath to a new version-pinned cache directory, whose agents/ ships
576
+ # the bundle's DEFAULT profile; without this re-apply step a switched
577
+ # operator would silently revert to that default on every update (context.md
578
+ # finding #4). Every failure path is a logged no-op, never a failure — a
579
+ # missing recorded profile, an unresolvable install path, or a profile the
580
+ # new bundle doesn't ship under agent-profiles/ all mean "nothing to
581
+ # re-apply", not an install error.
582
+ apply_recorded_profile_after_update() {
583
+ local runner="$1"
584
+ local install_path profile variant_dir copied=0 f name
585
+ install_path="$(resolve_claude_install_path "$runner")"
586
+ if [[ -z "$install_path" ]]; then
587
+ vecho " ↷ no resolvable marketplace install path — skipping recorded-profile re-apply"
588
+ return 0
589
+ fi
590
+ profile="$(recorded_profile "$runner")"
591
+ if [[ -z "$profile" ]]; then
592
+ vecho " ↷ no recorded model profile for claude — skipping recorded-profile re-apply"
593
+ return 0
594
+ fi
595
+ variant_dir="$install_path/agent-profiles/$profile"
596
+ if [[ ! -d "$variant_dir" ]]; then
597
+ echo " ⚠ recorded model profile '$profile' is not available under the updated bundle at $install_path — leaving its default agents in place" >&2
598
+ return 0
599
+ fi
600
+ mkdir -p "$install_path/agents"
601
+ for f in "$variant_dir/"massa-ai-*.md; do
602
+ [[ -f "$f" ]] || continue
603
+ name="$(basename "$f")"
604
+ cp "$f" "$install_path/agents/$name"
605
+ copied=$((copied + 1))
606
+ done
607
+ if [[ "$copied" -gt 0 ]]; then
608
+ vecho " ↷ re-applied recorded model profile '$profile' (${copied} files) to $install_path/agents"
609
+ else
610
+ vecho " ↷ recorded model profile '$profile' variant directory has no massa-ai-*.md files — nothing to re-apply"
611
+ fi
612
+ }
613
+
510
614
  # ── Plugin-registry registration (delegated to the claude CLI) ──────────────
511
615
  # The CLI owns the registry format, so it is the only supported way to make the
512
616
  # plugin appear in /plugin. Every failure path returns non-zero and the caller
@@ -546,6 +650,81 @@ unregister_claude_plugin() {
546
650
  claude plugin marketplace remove "$PLUGIN_MARKETPLACE" </dev/null >/dev/null 2>&1 || true
547
651
  }
548
652
 
653
+ # ── Marketplace cache refresh (CMR-01..05) ──────────────────────────────────
654
+ # `claude plugin install` is an idempotent no-op on an already-installed
655
+ # plugin, and the CLI serves a version-pinned cache snapshot — so an upgraded
656
+ # bundle never reaches an already-registered host by itself (observed: cache
657
+ # pinned at 1.28.0 while the checkout walked to 1.44.0). `claude plugin
658
+ # update` re-materializes the cache from a directory-source marketplace
659
+ # (verified against the real CLI, spec R1), so run it exactly when the served
660
+ # version is OLDER than the bundle — never on equal (no-op) or newer (that
661
+ # would downgrade; mirrors the harness "never downgrades" policy).
662
+
663
+ # Prints the version Claude actually serves for $PLUGIN_ID, or "" when it
664
+ # cannot be determined. Tolerant like recorded_profile: a missing/corrupt
665
+ # registry, a legacy non-array entry shape, or an absent entry all mean
666
+ # "unknown", never a failure. Claude-specific registry format — deliberately
667
+ # NOT in installer-shared.sh (Codex's registry differs; don't generalize one
668
+ # host's file shape).
669
+ claude_served_plugin_version() {
670
+ local runner="$1"
671
+ "$runner" - "$PLUGIN_REGISTRY" "$PLUGIN_ID" <<'NODE'
672
+ const fs = require("fs");
673
+ const [, , file, id] = process.argv;
674
+ try {
675
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
676
+ const entries = data && data.plugins ? data.plugins[id] : null;
677
+ if (!Array.isArray(entries) || entries.length === 0) process.exit(0);
678
+ const rec = entries.find((e) => e && e.scope === "user") ?? entries[0];
679
+ if (rec && typeof rec.version === "string") process.stdout.write(rec.version);
680
+ } catch { /* unknown */ }
681
+ NODE
682
+ }
683
+
684
+ # Fills PLUGIN_SERVED_VERSION. Every failure path degrades with a loud
685
+ # warning and never aborts the install (I1) — an empty result makes
686
+ # record_plugin_version skip the version claim, so the next harness run
687
+ # retries instead of trusting a lie.
688
+ refresh_marketplace_cache() {
689
+ local runner=""
690
+ if command -v node &>/dev/null; then runner="node"
691
+ elif command -v bun &>/dev/null; then runner="bun"
692
+ else
693
+ echo " ⚠ node or bun required to verify the served plugin version — skipping cache refresh" >&2
694
+ return 0
695
+ fi
696
+
697
+ local served bundle
698
+ served="$(claude_served_plugin_version "$runner")"
699
+ bundle="$(installer_bundle_version "$SCRIPT_DIR/package.json")"
700
+ if [[ -z "$served" ]]; then
701
+ echo " ⚠ could not read the served plugin version from $PLUGIN_REGISTRY — not recording a version claim" >&2
702
+ return 0
703
+ fi
704
+
705
+ if [[ "$(installer_compare_versions "$runner" "$served" "$bundle")" == "-1" ]]; then
706
+ if installer_host_cli_supports claude plugin update; then
707
+ if claude plugin update "$PLUGIN_ID" </dev/null >/dev/null 2>&1; then
708
+ vecho " + refreshed marketplace cache: ${served} → ${bundle}"
709
+ # CPP-07: an update just moved installPath to a new version-pinned
710
+ # cache directory; re-apply any previously recorded model profile
711
+ # before it silently reverts to the new bundle's default agents.
712
+ apply_recorded_profile_after_update "$runner"
713
+ else
714
+ echo " ⚠ 'claude plugin update ${PLUGIN_ID}' failed — Claude keeps serving ${served}; re-run the installer or run the update manually" >&2
715
+ fi
716
+ else
717
+ echo " ⚠ this claude CLI has no 'plugin update' — Claude keeps serving ${served}; update the CLI or reinstall the plugin" >&2
718
+ fi
719
+ # Re-read: record what the CLI serves NOW, whether or not the update
720
+ # succeeded (CMR-02 truthful record; a stale record keeps the harness
721
+ # version gate re-triggering until an update lands — self-healing).
722
+ served="$(claude_served_plugin_version "$runner")"
723
+ fi
724
+
725
+ PLUGIN_SERVED_VERSION="$served"
726
+ }
727
+
549
728
  # Strip artifacts a previous file-route install left behind. Called on the
550
729
  # plugin route, where the bundle supplies all three itself. Without this an
551
730
  # upgrading user keeps their old loose commands and merged hooks *and* gains
@@ -615,6 +794,7 @@ vecho "Installing massa-ai Claude Code plugin to: $TARGET"
615
794
  PLUGIN_ROUTE=0
616
795
  if register_claude_plugin; then
617
796
  PLUGIN_ROUTE=1
797
+ refresh_marketplace_cache
618
798
  fi
619
799
 
620
800
  command_count=0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massa-ai/claude-plugin",
3
- "version": "1.45.0",
3
+ "version": "1.47.0",
4
4
  "description": "massa-ai plugin for Claude Code — semantic code search, durable memory, symbol graph, and context compression",
5
5
  "files": [
6
6
  "agents",
@@ -59,7 +59,7 @@ npx @massa-ai/mcp-client --config-show
59
59
  npx @massa-ai/mcp-client --config-path
60
60
  npx @massa-ai/mcp-client --config-dir
61
61
  npx @massa-ai/mcp-client --config-init
62
- npx @massa-ai/mcp-client --config-set embedding.dimensions 4096
62
+ npx @massa-ai/mcp-client --config-set embedding.dimensions 2560
63
63
  ```
64
64
 
65
65
  Provider initialization supports Ollama plus `--mistral` and `--openai`