@zalom/plastic 1.0.0-alpha.12 → 1.0.0-alpha.13

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,11 +1,15 @@
1
1
  #!/bin/bash
2
- # plastic-hook-version: 1.0.0
3
- # Runs on SessionStart — checks npm for newer version, writes cache
2
+ # plastic-hook-version: 1.1.0
3
+ # Runs on SessionStart — checks npm for a newer version on the installed
4
+ # channel, writes cache. Channel-aware: queries dist-tags and picks the best
5
+ # upgrade target instead of blindly trusting the `latest` tag.
4
6
 
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
8
  PLASTIC_HOME="$HOME/.plastic"
6
9
  CACHE_DIR="$PLASTIC_HOME/.cache"
7
10
  CACHE_FILE="$CACHE_DIR/update-check.json"
8
11
  VERSION_FILE="$PLASTIC_HOME/VERSION"
12
+ SELECTOR="$SCRIPT_DIR/../scripts/select-update-target"
9
13
 
10
14
  mkdir -p "$CACHE_DIR"
11
15
 
@@ -21,21 +25,21 @@ fi
21
25
 
22
26
  # Background check — don't block session start
23
27
  (
24
- RAW=$(npm view @zalom/plastic version 2>/dev/null)
25
- LATEST=$(echo "$RAW" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$')
28
+ TAGS=$(npm view @zalom/plastic dist-tags --json 2>/dev/null)
26
29
 
27
- if [ -z "$LATEST" ]; then
30
+ if [ -z "$TAGS" ]; then
28
31
  exit 0
29
32
  fi
30
33
 
34
+ TARGET=$(printf '%s' "$TAGS" | ruby "$SELECTOR" "$CURRENT" 2>/dev/null)
31
35
  CHECKED=$(date -u +%Y-%m-%dT%H:%M:%SZ)
32
36
 
33
- if [ "$LATEST" != "$CURRENT" ]; then
37
+ if [ -n "$TARGET" ]; then
34
38
  printf '{"current":"%s","latest":"%s","checked":"%s","updateAvailable":true}\n' \
35
- "$CURRENT" "$LATEST" "$CHECKED" > "$CACHE_FILE"
39
+ "$CURRENT" "$TARGET" "$CHECKED" > "$CACHE_FILE"
36
40
  else
37
41
  printf '{"current":"%s","latest":"%s","checked":"%s","updateAvailable":false}\n' \
38
- "$CURRENT" "$LATEST" "$CHECKED" > "$CACHE_FILE"
42
+ "$CURRENT" "$CURRENT" "$CHECKED" > "$CACHE_FILE"
39
43
  fi
40
44
  ) &
41
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.0.0-alpha.12",
3
+ "version": "1.0.0-alpha.13",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -159,6 +159,7 @@ def distribute(mode)
159
159
  "deprecations.yml" => "deprecations.yml",
160
160
  "scripts/folgezettel-id" => "scripts/folgezettel-id",
161
161
  "scripts/read-config" => "scripts/read-config",
162
+ "scripts/select-update-target" => "scripts/select-update-target",
162
163
  "scripts/hook-session-start" => "scripts/hook-session-start",
163
164
  "scripts/hook-continue" => "scripts/hook-continue",
164
165
  "scripts/hook-future-intent-check" => "scripts/hook-future-intent-check",
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # plastic-script-version: 1.0.0
3
+ # Channel-aware update target selector.
4
+ #
5
+ # Usage: npm view @zalom/plastic dist-tags --json | select-update-target <current-version>
6
+ #
7
+ # Prints the best update target to stdout, or nothing if already up to date.
8
+ #
9
+ # Rule: target = the highest version, among the dist-tags reachable from the
10
+ # installed channel, that is strictly greater than the installed version (semver).
11
+ # alpha installed -> reachable: alpha, beta, latest
12
+ # beta installed -> reachable: beta, latest (never downgrade to alpha)
13
+ # stable installed -> reachable: latest (stable only)
14
+ # A prerelease (1.1.0-alpha.1) is LOWER than its release (1.1.0); the `latest`
15
+ # placeholder (0.0.1) is naturally excluded as lower than any 1.0.0-alpha.N.
16
+
17
+ require "json"
18
+
19
+ # Parse "MAJOR.MINOR.PATCH[-prerelease]" into a comparable structure.
20
+ # Returns nil for anything that isn't a clean semver string.
21
+ def parse(version)
22
+ m = /\A(\d+)\.(\d+)\.(\d+)(?:-(.+))?\z/.match(version.to_s.strip)
23
+ return nil unless m
24
+ { maj: m[1].to_i, min: m[2].to_i, pat: m[3].to_i,
25
+ pre: m[4] ? m[4].split(".") : nil }
26
+ end
27
+
28
+ # Semver precedence (§11). Returns -1, 0, or 1.
29
+ def compare(a, b)
30
+ %i[maj min pat].each do |k|
31
+ return a[k] <=> b[k] unless a[k] == b[k]
32
+ end
33
+ # A version with no prerelease is greater than one with a prerelease.
34
+ return 0 if a[:pre].nil? && b[:pre].nil?
35
+ return 1 if a[:pre].nil?
36
+ return -1 if b[:pre].nil?
37
+ [a[:pre].length, b[:pre].length].max.times do |i|
38
+ x = a[:pre][i]
39
+ y = b[:pre][i]
40
+ return -1 if x.nil?
41
+ return 1 if y.nil?
42
+ xn = x.match?(/\A\d+\z/)
43
+ yn = y.match?(/\A\d+\z/)
44
+ if xn && yn
45
+ return x.to_i <=> y.to_i unless x.to_i == y.to_i
46
+ elsif xn
47
+ return -1 # numeric identifiers rank lower than alphanumeric
48
+ elsif yn
49
+ return 1
50
+ elsif x != y
51
+ return x <=> y
52
+ end
53
+ end
54
+ 0
55
+ end
56
+
57
+ def channel(version)
58
+ return "alpha" if version.include?("-alpha")
59
+ return "beta" if version.include?("-beta")
60
+ "stable"
61
+ end
62
+
63
+ REACH = {
64
+ "alpha" => %w[alpha beta latest],
65
+ "beta" => %w[beta latest],
66
+ "stable" => %w[latest],
67
+ }.freeze
68
+
69
+ current = ARGV[0]
70
+ cur = parse(current)
71
+ exit 0 unless cur
72
+
73
+ begin
74
+ tags = JSON.parse($stdin.read)
75
+ rescue JSON::ParserError, StandardError
76
+ exit 0
77
+ end
78
+ exit 0 unless tags.is_a?(Hash)
79
+
80
+ best = nil
81
+ best_parsed = nil
82
+ REACH[channel(current)].each do |tag|
83
+ raw = tags[tag]
84
+ next unless raw
85
+ parsed = parse(raw)
86
+ next unless parsed
87
+ if compare(parsed, cur) > 0 && (best_parsed.nil? || compare(parsed, best_parsed) > 0)
88
+ best = raw
89
+ best_parsed = parsed
90
+ end
91
+ end
92
+
93
+ print best if best