@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.
Files changed (2) hide show
  1. package/hooks/statusline +126 -42
  2. package/package.json +1 -1
package/hooks/statusline CHANGED
@@ -1,56 +1,140 @@
1
1
  #!/bin/bash
2
- # plastic-hook-version: 2.0.0
3
- # StatusLine hook chains user's original statusline, adds Plastic info
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
- # --- Chain original statusline ---
8
- ORIGINAL_CONFIG="$HOME/.plastic/.cache/original-statusline.json"
9
- ORIGINAL_OUTPUT=""
10
- if [ -f "$ORIGINAL_CONFIG" ]; then
11
- ORIGINAL_CMD=$(grep -o '"command":"[^"]*"' "$ORIGINAL_CONFIG" 2>/dev/null | head -1 | cut -d'"' -f4)
12
- if [ -z "$ORIGINAL_CMD" ]; then
13
- ORIGINAL_CMD=$(grep -o '"command": "[^"]*"' "$ORIGINAL_CONFIG" 2>/dev/null | head -1 | sed 's/.*"command": "//;s/".*//')
14
- fi
15
- if [ -n "$ORIGINAL_CMD" ] && [ -x "$ORIGINAL_CMD" ]; then
16
- ORIGINAL_OUTPUT=$(echo "$INPUT" | "$ORIGINAL_CMD" 2>/dev/null)
17
- fi
18
- fi
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
- # --- Plastic status ---
21
- PLASTIC_PARTS=""
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
- # Active intent from INDEX.md
24
- INDEX="$HOME/.plastic/INDEX.md"
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
- # Update notification
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
- UPDATE_AVAILABLE=$(grep -o '"updateAvailable":true' "$CACHE_FILE" 2>/dev/null)
37
- if [ -n "$UPDATE_AVAILABLE" ]; then
38
- LATEST=$(grep -o '"latest":"[^"]*"' "$CACHE_FILE" 2>/dev/null | head -1 | cut -d'"' -f4)
39
- CURRENT=$(grep -o '"current":"[^"]*"' "$CACHE_FILE" 2>/dev/null | head -1 | cut -d'"' -f4)
40
- UPDATE_MSG="plastic update: $CURRENT -> $LATEST"
41
- if [ -n "$PLASTIC_PARTS" ]; then
42
- PLASTIC_PARTS="$PLASTIC_PARTS | $UPDATE_MSG"
43
- else
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
- # --- Combine output ---
50
- if [ -n "$ORIGINAL_OUTPUT" ] && [ -n "$PLASTIC_PARTS" ]; then
51
- echo "$ORIGINAL_OUTPUT | $PLASTIC_PARTS"
52
- elif [ -n "$ORIGINAL_OUTPUT" ]; then
53
- echo "$ORIGINAL_OUTPUT"
54
- elif [ -n "$PLASTIC_PARTS" ]; then
55
- echo "$PLASTIC_PARTS"
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {