@zalom/plastic 1.0.0-beta.3 → 1.0.0-beta.4
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/hooks/statusline +126 -42
- package/package.json +1 -1
package/hooks/statusline
CHANGED
|
@@ -1,56 +1,140 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# plastic-hook-version:
|
|
3
|
-
# StatusLine hook
|
|
2
|
+
# plastic-hook-version: 3.0.0
|
|
3
|
+
# StatusLine hook - Plastic owns the full statusline (no chaining).
|
|
4
|
+
# Renders: {model} - Plastic {version}[ -> {next}] - {path} - [{project}: {id} {title}]
|
|
5
|
+
# Pure bash (macOS 3.2 compatible). No ruby, no jq. Reads stdin once; small file reads only.
|
|
4
6
|
|
|
5
7
|
INPUT=$(cat)
|
|
6
8
|
|
|
7
|
-
# ---
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
# --- Colors (ANSI-C quoting; bash 3.2 supports $'\033') ---
|
|
10
|
+
C_MODEL=$'\033[38;2;90;140;220m' # navy blue - model (lightened for legibility)
|
|
11
|
+
C_VER=$'\033[37m' # white - "Plastic {version}" and update arrow
|
|
12
|
+
C_NEXT=$'\033[33m' # yellow - next version
|
|
13
|
+
C_PATH=$'\033[38;2;128;134;144m' # gray - path (matches dim UI text)
|
|
14
|
+
C_BRACK=$'\033[33m' # yellow - work-unit brackets
|
|
15
|
+
C_PROJ=$'\033[38;2;249;140;30m' # orange - project name
|
|
16
|
+
C_ID=$'\033[38;2;45;212;191m' # teal - intent id (accent between orange + yellow)
|
|
17
|
+
C_INTENT=$'\033[33m' # yellow - intent title
|
|
18
|
+
C_SEP=$'\033[38;2;128;134;144m' # gray - separators
|
|
19
|
+
RST=$'\033[0m'
|
|
20
|
+
SEP="${C_SEP} "$'\302\267'" ${RST}" # " . " middot, dim
|
|
19
21
|
|
|
20
|
-
# ---
|
|
21
|
-
|
|
22
|
+
# --- Extract a flat JSON string field from stdin: json_str <key> ---
|
|
23
|
+
json_str() {
|
|
24
|
+
printf '%s' "$INPUT" \
|
|
25
|
+
| grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" \
|
|
26
|
+
| head -1 \
|
|
27
|
+
| sed "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"//;s/\"$//"
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
if [ -f "$INDEX" ]; then
|
|
26
|
-
ACTIVE=$(sed -n '/^## Active$/,/^## /{/^- \[/p;}' "$INDEX" | head -1)
|
|
27
|
-
if [ -n "$ACTIVE" ]; then
|
|
28
|
-
INTENT=$(echo "$ACTIVE" | sed 's/^- \[\([^]]*\)\].*/\1/')
|
|
29
|
-
PLASTIC_PARTS="plastic: ${INTENT}"
|
|
30
|
-
fi
|
|
31
|
-
fi
|
|
30
|
+
# --- Model (model.display_name) ---
|
|
31
|
+
MODEL=$(json_str "display_name")
|
|
32
32
|
|
|
33
|
-
#
|
|
33
|
+
# --- Path (workspace.current_dir, fallback top-level cwd) ---
|
|
34
|
+
REAL_CWD=$(json_str "current_dir")
|
|
35
|
+
[ -z "$REAL_CWD" ] && REAL_CWD=$(json_str "cwd")
|
|
36
|
+
# Abbreviate $HOME -> ~
|
|
37
|
+
CWD="$REAL_CWD"
|
|
38
|
+
case "$CWD" in
|
|
39
|
+
"$HOME") CWD="~" ;;
|
|
40
|
+
"$HOME"/*) CWD="~${CWD#"$HOME"}" ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
# --- Plastic version ---
|
|
44
|
+
VERSION=""
|
|
45
|
+
[ -f "$HOME/.plastic/VERSION" ] && VERSION=$(tr -d ' \t\n' < "$HOME/.plastic/VERSION")
|
|
46
|
+
|
|
47
|
+
# --- Update (next version when one is available) ---
|
|
48
|
+
NEXT=""
|
|
34
49
|
CACHE_FILE="$HOME/.plastic/.cache/update-check.json"
|
|
35
50
|
if [ -f "$CACHE_FILE" ]; then
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
PLASTIC_PARTS="$UPDATE_MSG"
|
|
51
|
+
if grep -q '"updateAvailable"[[:space:]]*:[[:space:]]*true' "$CACHE_FILE" 2>/dev/null; then
|
|
52
|
+
LATEST=$(grep -o '"latest"[[:space:]]*:[[:space:]]*"[^"]*"' "$CACHE_FILE" 2>/dev/null \
|
|
53
|
+
| head -1 | sed 's/.*"latest"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
54
|
+
if [ -n "$LATEST" ]; then
|
|
55
|
+
case "$LATEST" in
|
|
56
|
+
*-*) NEXT="${LATEST##*-}" ;; # 1.0.0-beta.3 -> beta.3
|
|
57
|
+
*) NEXT="$LATEST" ;;
|
|
58
|
+
esac
|
|
45
59
|
fi
|
|
46
60
|
fi
|
|
47
61
|
fi
|
|
48
62
|
|
|
49
|
-
# ---
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
# --- Scope resolution: cwd -> registered project store, else global ---
|
|
64
|
+
# Match the longest registered project path that prefixes the real cwd.
|
|
65
|
+
PROJECTS_YML="$HOME/.plastic/projects.yml"
|
|
66
|
+
SLUG=""
|
|
67
|
+
if [ -n "$REAL_CWD" ] && [ -f "$PROJECTS_YML" ]; then
|
|
68
|
+
SLUG=$(awk -v cwd="$REAL_CWD" '
|
|
69
|
+
/^ [A-Za-z0-9_.-]+:[[:space:]]*$/ { s=$1; sub(/:$/,"",s); next }
|
|
70
|
+
/^ path:/ {
|
|
71
|
+
p=$0; sub(/^[[:space:]]*path:[[:space:]]*/,"",p); gsub(/"/,"",p);
|
|
72
|
+
if (length(p)>0 && (cwd==p || index(cwd, p"/")==1) && length(p)>best) { best=length(p); win=s }
|
|
73
|
+
}
|
|
74
|
+
END { if (win!="") print win }
|
|
75
|
+
' "$PROJECTS_YML")
|
|
56
76
|
fi
|
|
77
|
+
|
|
78
|
+
if [ -n "$SLUG" ]; then
|
|
79
|
+
INDEX="$HOME/.plastic/projects/$SLUG/INDEX.md"
|
|
80
|
+
PROJ_LABEL="$SLUG"
|
|
81
|
+
else
|
|
82
|
+
INDEX="$HOME/.plastic/INDEX.md"
|
|
83
|
+
PROJ_LABEL="global"
|
|
84
|
+
fi
|
|
85
|
+
INDEX_DIR=$(dirname "$INDEX")
|
|
86
|
+
|
|
87
|
+
# --- Most-recently-accessed active intent (mirror dashboard: last savepoint ts) ---
|
|
88
|
+
WORK=""
|
|
89
|
+
if [ -f "$INDEX" ]; then
|
|
90
|
+
BEST_TS=""
|
|
91
|
+
while IFS= read -r line; do
|
|
92
|
+
[ -z "$line" ] && continue
|
|
93
|
+
inside=${line#*[}; inside=${inside%%]*} # "{id} - {title}"
|
|
94
|
+
dir=${line#*](store/}; dir=${dir%%/*} # intent directory name
|
|
95
|
+
[ -z "$dir" ] && continue
|
|
96
|
+
sp="$INDEX_DIR/store/$dir/savepoint.md"
|
|
97
|
+
ts=""
|
|
98
|
+
if [ -f "$sp" ]; then
|
|
99
|
+
ts=$(grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z' "$sp" 2>/dev/null | tail -1)
|
|
100
|
+
fi
|
|
101
|
+
# ISO8601 sorts lexicographically; keep the newest. Empty ts sorts lowest.
|
|
102
|
+
if [ -z "$BEST_TS" ] || { [ -n "$ts" ] && [ "$ts" \> "$BEST_TS" ]; }; then
|
|
103
|
+
BEST_TS="$ts"
|
|
104
|
+
WORK="$inside"
|
|
105
|
+
fi
|
|
106
|
+
done <<EOF
|
|
107
|
+
$(sed -n '/^## Active$/,/^## /{/^- \[/p;}' "$INDEX")
|
|
108
|
+
EOF
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# --- Build the work-unit "[{project}: {id} {title}]" with title truncation ---
|
|
112
|
+
WORK_SEG=""
|
|
113
|
+
if [ -n "$WORK" ]; then
|
|
114
|
+
# Split "{id} - {title}" on the em-dash (U+2014).
|
|
115
|
+
EMDASH=$'\342\200\224'
|
|
116
|
+
id=${WORK%% $EMDASH *}
|
|
117
|
+
title=${WORK#* $EMDASH }
|
|
118
|
+
# Fallback if the em-dash form is absent: split on first space.
|
|
119
|
+
if [ "$id" = "$WORK" ]; then id=${WORK%% *}; title=${WORK#* }; fi
|
|
120
|
+
# Truncate long titles (~40 chars).
|
|
121
|
+
if [ ${#title} -gt 40 ]; then title="$(printf '%.40s' "$title")..."; fi
|
|
122
|
+
WORK_SEG="${C_BRACK}[${RST}${C_PROJ}${PROJ_LABEL}${RST}${C_INTENT}: ${RST}${C_ID}${id}${RST}${C_INTENT} ${title}${RST}${C_BRACK}]${RST}"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# --- Assemble segments in order, joined by " . " ---
|
|
126
|
+
PARTS=""
|
|
127
|
+
add() { [ -z "$1" ] && return; if [ -z "$PARTS" ]; then PARTS="$1"; else PARTS="$PARTS$SEP$1"; fi; }
|
|
128
|
+
|
|
129
|
+
# Order: stable values first (model, work-unit), then the volatile-length
|
|
130
|
+
# values last (version, path) so the line does not visually jump as they change.
|
|
131
|
+
[ -n "$MODEL" ] && add "${C_MODEL}${MODEL}${RST}"
|
|
132
|
+
[ -n "$WORK_SEG" ] && add "$WORK_SEG"
|
|
133
|
+
if [ -n "$VERSION" ]; then
|
|
134
|
+
vseg="${C_VER}Plastic ${VERSION}${RST}"
|
|
135
|
+
[ -n "$NEXT" ] && vseg="${vseg}${C_VER} "$'\342\206\222'" ${RST}${C_NEXT}${NEXT}${RST}"
|
|
136
|
+
add "$vseg"
|
|
137
|
+
fi
|
|
138
|
+
[ -n "$CWD" ] && add "${C_PATH}${CWD}${RST}"
|
|
139
|
+
|
|
140
|
+
printf '%s\n' "$PARTS"
|