@massa-ai/claude-plugin 1.11.0 → 1.12.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.11.0",
3
+ "version": "1.12.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
@@ -291,7 +291,13 @@ if (typeof data.platforms !== "object" || data.platforms === null || Array.isArr
291
291
  data.platforms = {};
292
292
  }
293
293
  data.version = 2;
294
+ const prev = data.platforms[host];
294
295
  data.platforms[host] = { root, skillsOwner: "plugin", skills: ["massa-ai", "persona-router"] };
296
+ // The whole-record replace must not drop the plugin version record a previous
297
+ // successful install wrote (R2) — re-attach it.
298
+ if (prev && typeof prev === "object" && !Array.isArray(prev) && prev.plugin && typeof prev.plugin === "object") {
299
+ data.platforms[host].plugin = prev.plugin;
300
+ }
295
301
  fs.mkdirSync(path.dirname(file), { recursive: true });
296
302
  fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
297
303
  NODE
@@ -319,28 +325,87 @@ try {
319
325
  }
320
326
  NODE
321
327
  )"
322
- [[ "$raw_owner" == "plugin" ]] || return 0
323
-
324
- local name
325
- for name in massa-ai persona-router; do
326
- rm -rf "$HARNESS_SKILLS_DIR/$name"
327
- done
328
- rmdir "$HARNESS_SKILLS_DIR" 2>/dev/null || true
329
- echo " - removed plugin-owned harness skills from $HARNESS_SKILLS_DIR"
328
+ [[ "$raw_owner" == "plugin" ]] && {
329
+ local name
330
+ for name in massa-ai persona-router; do
331
+ rm -rf "$HARNESS_SKILLS_DIR/$name"
332
+ done
333
+ rmdir "$HARNESS_SKILLS_DIR" 2>/dev/null || true
334
+ echo " - removed plugin-owned harness skills from $HARNESS_SKILLS_DIR"
335
+ }
330
336
 
337
+ # AC-15: a plugin-owned platform keeps the whole-record delete (clean-slate
338
+ # semantics unchanged); any other platform loses only its plugin version
339
+ # record while root/skills/skillsOwner survive (Plan Challenge C-4).
331
340
  "$runner" - "$HARNESS_STATE_FILE" "$HARNESS_HOST" <<'NODE'
332
341
  const fs = require("fs");
333
342
  const [, , file, host] = process.argv;
334
343
  try {
335
344
  const data = JSON.parse(fs.readFileSync(file, "utf8"));
336
- if (data && data.platforms && data.platforms[host] && data.platforms[host].skillsOwner === "plugin") {
337
- delete data.platforms[host];
338
- fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
345
+ const rec = data && data.platforms && data.platforms[host];
346
+ if (rec && typeof rec === "object") {
347
+ if (rec.skillsOwner === "plugin") {
348
+ delete data.platforms[host];
349
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
350
+ } else if (rec.plugin && typeof rec.plugin === "object") {
351
+ delete rec.plugin;
352
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
353
+ }
339
354
  }
340
355
  } catch { /* nothing to clean up */ }
341
356
  NODE
342
357
  }
343
358
 
359
+ # ── Plugin version recording (PAI-03/04/07) ─────────────────────────────────
360
+ # Why: the harness gates re-runs on the recorded bundle version, so a
361
+ # successful install records it — but ONLY as the final step of the
362
+ # install path. A version written before a later step fails would mark a
363
+ # broken install "current" and skip it forever (AC-16, Plan Challenge C-1).
364
+ # Impacts: PAI-03 record, PAI-04 upgrade trigger, PAI-07 uninstall, AC-3/16.
365
+ # Test: bash scripts/tests/test-plugin-auto-install.sh (Section 3 e2e chain)
366
+ record_plugin_version() {
367
+ local runner=""
368
+ if command -v node &>/dev/null; then runner="node"
369
+ elif command -v bun &>/dev/null; then runner="bun"
370
+ else
371
+ echo " ⚠ node or bun required to record the plugin version — next run will reinstall" >&2
372
+ return 0
373
+ fi
374
+
375
+ local version installed_at
376
+ version="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$SCRIPT_DIR/package.json" | head -n 1)"
377
+ installed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
378
+
379
+ # Tolerant of a corrupt/missing state file (rewrites a minimal valid one —
380
+ # AC-8). A record-write failure warns but never fails the install: the next
381
+ # run treats the host as unknown-version and reinstalls.
382
+ "$runner" - "$HARNESS_STATE_FILE" "$HARNESS_HOST" "$TARGET" "$version" "$installed_at" <<'NODE' || \
383
+ echo " ⚠ could not record the plugin version — next run will reinstall (unknown version)" >&2
384
+ const fs = require("fs");
385
+ const path = require("path");
386
+ const [, , file, host, root, version, installedAt] = process.argv;
387
+ let data = { version: 2, platforms: {} };
388
+ try {
389
+ const existing = JSON.parse(fs.readFileSync(file, "utf8"));
390
+ if (existing && typeof existing === "object" && !Array.isArray(existing)) data = existing;
391
+ } catch { /* fresh */ }
392
+ if (typeof data.platforms !== "object" || data.platforms === null || Array.isArray(data.platforms)) {
393
+ data.platforms = {};
394
+ }
395
+ data.version = 2;
396
+ // Preserve every unrelated field; a missing record gets the minimal shape the
397
+ // strict skills reader accepts (non-empty root + a skills list).
398
+ const rec =
399
+ data.platforms[host] && typeof data.platforms[host] === "object" && !Array.isArray(data.platforms[host])
400
+ ? data.platforms[host]
401
+ : { root, skillsOwner: "plugin", skills: [] };
402
+ rec.plugin = { version, installedAt };
403
+ data.platforms[host] = rec;
404
+ fs.mkdirSync(path.dirname(file), { recursive: true });
405
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
406
+ NODE
407
+ }
408
+
344
409
  # ── Plugin-registry registration (delegated to the claude CLI) ──────────────
345
410
  # The CLI owns the registry format, so it is the only supported way to make the
346
411
  # plugin appear in /plugin. Every failure path returns non-zero and the caller
@@ -546,4 +611,8 @@ vecho " 1. Try: /massa-ai-status"
546
611
  vecho " 2. Try: /massa-ai-map (on an indexed project)"
547
612
  # ~/.claude.json, not ~/.claude/settings.json — Claude Code reads MCP
548
613
  # definitions from the former; settings.json only holds approval controls.
549
- vecho "💡 Hooks are wired by this plugin; MCP is registered in ~/.claude.json by scripts/install-agents.sh (single writer)."
614
+ vecho "💡 Hooks are wired by this plugin; MCP is registered in ~/.claude.json by scripts/install-agents.sh (single writer)."
615
+
616
+ # Final step of the install path: record the bundle version. Reached only when
617
+ # every fallible step succeeded (AC-16) — never on --uninstall (exits above).
618
+ record_plugin_version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massa-ai/claude-plugin",
3
- "version": "1.11.0",
3
+ "version": "1.12.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",