@zalom/plastic 1.0.0-beta.3 → 1.0.0-beta.30
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/PLASTIC.md +293 -7
- package/README.md +10 -0
- package/agents/plastic-brainstorming.md +2 -2
- package/agents/plastic-enforcer.md +17 -2
- package/agents/plastic-executor.md +3 -3
- package/agents/plastic-future-intent-researcher.md +2 -1
- package/agents/plastic-intent-curator.md +15 -7
- package/agents/plastic-intent-discovery.md +37 -0
- package/agents/plastic-planner.md +2 -2
- package/agents/plastic-spec-specialist.md +2 -2
- package/hooks/code-gate +10 -1
- package/hooks/hooks.json +28 -3
- package/hooks/lock-gate +21 -0
- package/hooks/retrieval-gate +10 -0
- package/hooks/savepoint-pre +10 -0
- package/hooks/statusline +150 -41
- package/package.json +1 -1
- package/scripts/agent-report +21 -0
- package/scripts/doctor.rb +404 -5
- package/scripts/hook-auto-arm +1 -1
- package/scripts/hook-bash-gate +22 -4
- package/scripts/hook-code-gate +11 -6
- package/scripts/hook-create-gate +45 -9
- package/scripts/hook-gate-check +24 -23
- package/scripts/hook-lock-gate +83 -0
- package/scripts/hook-retrieval-gate +148 -0
- package/scripts/hook-savepoint-pre +32 -0
- package/scripts/hook-session-start +1 -1
- package/scripts/insight-append +51 -0
- package/scripts/lib/agent_models.rb +43 -0
- package/scripts/lib/bridge.rb +625 -47
- package/scripts/lib/frontmatter_writer.rb +130 -0
- package/scripts/lib/graph_rebuild.rb +328 -0
- package/scripts/lib/hook_registry.rb +95 -0
- package/scripts/lib/insights.rb +86 -0
- package/scripts/lib/installer_core.rb +64 -54
- package/scripts/lib/link_suggestions.rb +319 -0
- package/scripts/lib/links_projection.rb +160 -0
- package/scripts/lib/links_section.rb +207 -0
- package/scripts/lib/lock.rb +375 -0
- package/scripts/lib/power_tools.rb +75 -0
- package/scripts/lib/qmd_hook.rb +38 -25
- package/scripts/lib/qmd_sync.rb +36 -0
- package/scripts/lib/retrieval_gate.rb +211 -0
- package/scripts/lib/worktree.rb +384 -0
- package/scripts/link-suggest +213 -0
- package/scripts/new-intent +138 -29
- package/scripts/plastic-lock +164 -0
- package/scripts/project-links +287 -0
- package/scripts/qmd-sync +50 -3
- package/scripts/read-config +4 -0
- package/scripts/rebuild-graph +244 -0
- package/scripts/spawn-preamble +11 -3
- package/skills/auto/SKILL.md +98 -16
- package/skills/auto/evals/evals.json +48 -0
- package/skills/auto/references/agent-architecture.md +7 -4
- package/skills/auto/references/agent-report-contract.md +36 -1
- package/skills/brainstorming/SKILL.md +1 -0
- package/skills/brainstorming/evals/evals.json +22 -0
- package/skills/continuing/SKILL.md +30 -8
- package/skills/continuing/evals/evals.json +9 -0
- package/skills/creating-intent/SKILL.md +16 -2
- package/skills/creating-intent/evals/evals.json +16 -0
- package/skills/creating-intent/references/lifecycle.md +9 -4
- package/skills/creating-skills/SKILL.md +65 -0
- package/skills/creating-skills/evals/evals.json +108 -0
- package/skills/creating-skills/references/agents.md +168 -0
- package/skills/creating-skills/references/evals.md +41 -0
- package/skills/creating-skills/references/hooks.md +248 -0
- package/skills/creating-skills/references/progressive-disclosure.md +176 -0
- package/skills/creating-skills/references/scripts.md +166 -0
- package/skills/creating-skills/references/skills.md +165 -0
- package/skills/creating-skills/scripts/scaffold.rb +313 -0
- package/skills/dashboard/SKILL.md +5 -0
- package/skills/dashboard/evals/evals.json +22 -0
- package/skills/doctor/SKILL.md +4 -1
- package/skills/executing-plan/SKILL.md +4 -4
- package/skills/humanizer/SKILL.md +39 -0
- package/skills/humanizer/evals/evals.json +70 -0
- package/skills/humanizer/references/always-on-snippet.md +9 -0
- package/skills/humanizer/references/examples.md +48 -0
- package/skills/intent-curator/SKILL.md +7 -1
- package/skills/intent-curator/evals/evals.json +22 -0
- package/skills/intent-discovery/SKILL.md +46 -0
- package/skills/intent-starting/SKILL.md +127 -0
- package/skills/intent-starting/evals/evals.json +117 -0
- package/skills/intent-starting/references/boarding-matrix.md +35 -0
- package/skills/linking-intents/SKILL.md +54 -12
- package/skills/linking-intents/evals/evals.json +22 -0
- package/skills/linking-intents/references/zettelkasten.md +7 -0
- package/skills/lock/SKILL.md +41 -0
- package/skills/managing-index/SKILL.md +12 -0
- package/skills/managing-index/evals/evals.json +22 -0
- package/skills/managing-index/references/zettelkasten-linking.md +6 -1
- package/skills/releasing/SKILL.md +32 -0
- package/skills/research/SKILL.md +8 -0
- package/skills/research/evals/evals.json +22 -0
- package/templates/config.yml +8 -0
- package/templates/outcome.md +3 -0
- package/templates/revisions.md +58 -0
- package/skills/writing-instructions/SKILL.md +0 -159
- package/skills/writing-instructions/references/agentskills-spec.md +0 -135
package/hooks/hooks.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"PreCompact": [
|
|
21
21
|
{
|
|
22
|
-
"matcher": "
|
|
22
|
+
"matcher": "",
|
|
23
23
|
"hooks": [
|
|
24
24
|
{
|
|
25
25
|
"type": "command",
|
|
@@ -31,17 +31,32 @@
|
|
|
31
31
|
],
|
|
32
32
|
"PreToolUse": [
|
|
33
33
|
{
|
|
34
|
-
"matcher": "Write|Edit|NotebookEdit",
|
|
34
|
+
"matcher": "Write|Edit|NotebookEdit|mcp__serena__replace_content|mcp__serena__replace_symbol_body|mcp__serena__insert_after_symbol|mcp__serena__insert_before_symbol|mcp__serena__safe_delete_symbol|mcp__serena__rename_symbol",
|
|
35
35
|
"hooks": [
|
|
36
36
|
{
|
|
37
37
|
"type": "command",
|
|
38
38
|
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" code-gate",
|
|
39
39
|
"statusMessage": "Checking lifecycle gate..."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "command",
|
|
43
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" lock-gate",
|
|
44
|
+
"statusMessage": "Checking lock gate..."
|
|
40
45
|
}
|
|
41
46
|
]
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
|
-
"matcher": "Write",
|
|
49
|
+
"matcher": "Write|Edit",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" savepoint-pre",
|
|
54
|
+
"statusMessage": "Recording stage start..."
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"matcher": "Write|Edit|mcp__serena__replace_content|mcp__serena__replace_symbol_body|mcp__serena__insert_after_symbol|mcp__serena__insert_before_symbol|mcp__serena__safe_delete_symbol|mcp__serena__rename_symbol",
|
|
45
60
|
"hooks": [
|
|
46
61
|
{
|
|
47
62
|
"type": "command",
|
|
@@ -59,6 +74,16 @@
|
|
|
59
74
|
"statusMessage": "Checking lifecycle gate..."
|
|
60
75
|
}
|
|
61
76
|
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"matcher": "Bash|Read|Grep|Glob",
|
|
80
|
+
"hooks": [
|
|
81
|
+
{
|
|
82
|
+
"type": "command",
|
|
83
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" retrieval-gate",
|
|
84
|
+
"statusMessage": "Checking retrieval gate..."
|
|
85
|
+
}
|
|
86
|
+
]
|
|
62
87
|
}
|
|
63
88
|
],
|
|
64
89
|
"PostToolUse": [
|
package/hooks/lock-gate
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
INPUT=$(cat)
|
|
3
|
+
FILE_PATH=$(echo "$INPUT" | ruby -rjson -e '
|
|
4
|
+
data = JSON.parse(STDIN.read)
|
|
5
|
+
ti = data["tool_input"] || data["tool_params"] || {}
|
|
6
|
+
path = ti["file_path"] || ti["notebook_path"] || ti["relative_path"] || ""
|
|
7
|
+
if !path.empty? && !path.start_with?("/")
|
|
8
|
+
root = ti["project_root"] || data["cwd"] || ""
|
|
9
|
+
path = File.join(root, path) unless root.empty?
|
|
10
|
+
end
|
|
11
|
+
puts path
|
|
12
|
+
' 2>/dev/null)
|
|
13
|
+
|
|
14
|
+
if [ -z "$FILE_PATH" ]; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
SESSION_ID=$(echo "$INPUT" | ruby -rjson -e 'data = JSON.parse(STDIN.read); puts data.dig("session_id") || ""' 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
ruby "$SCRIPT_DIR/../scripts/hook-lock-gate" "$FILE_PATH" "$SESSION_ID"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Retrieval-gate hook launcher (intent 84, Lever 2). No-op when there is no
|
|
3
|
+
# global store. Passes the PreToolUse JSON through on stdin (exec inherits it)
|
|
4
|
+
# and the plastic_home as ARGV[0], mirroring the qmd-search launcher.
|
|
5
|
+
GLOBAL_INDEX="$HOME/.plastic/INDEX.md"
|
|
6
|
+
if [ ! -f "$GLOBAL_INDEX" ]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
exec ruby "$SCRIPT_DIR/../scripts/hook-retrieval-gate" "$HOME/.plastic"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
INPUT=$(cat)
|
|
3
|
+
FILE_PATH=$(echo "$INPUT" | ruby -rjson -e 'data = JSON.parse(STDIN.read); puts data.dig("tool_params", "file_path") || data.dig("tool_input", "file_path") || ""' 2>/dev/null)
|
|
4
|
+
|
|
5
|
+
if [ -z "$FILE_PATH" ]; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
ruby "$SCRIPT_DIR/../scripts/hook-savepoint-pre" "$FILE_PATH"
|
package/hooks/statusline
CHANGED
|
@@ -1,56 +1,165 @@
|
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|
|
29
|
+
|
|
30
|
+
# --- Model (model.display_name) ---
|
|
31
|
+
MODEL=$(json_str "display_name")
|
|
32
|
+
|
|
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=""
|
|
49
|
+
CACHE_FILE="$HOME/.plastic/.cache/update-check.json"
|
|
50
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
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
|
|
59
|
+
fi
|
|
17
60
|
fi
|
|
18
61
|
fi
|
|
19
62
|
|
|
20
|
-
# ---
|
|
21
|
-
|
|
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")
|
|
76
|
+
fi
|
|
22
77
|
|
|
23
|
-
|
|
24
|
-
INDEX="$HOME/.plastic/INDEX.md"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
PLASTIC_PARTS="plastic: ${INTENT}"
|
|
30
|
-
fi
|
|
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"
|
|
31
84
|
fi
|
|
85
|
+
INDEX_DIR=$(dirname "$INDEX")
|
|
32
86
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
87
|
+
# --- Current-session work-unit: the live bridge wins over savepoint recency ---
|
|
88
|
+
# The statusline receives the real session_id on stdin; the session's bridge
|
|
89
|
+
# (plastic-{session_id}.json) is the authoritative "what THIS session is driving".
|
|
90
|
+
# This beats the shared, savepoint-recency heuristic so parallel sessions on one
|
|
91
|
+
# store no longer overwrite each other's line. grep/sed only - no jq, no ruby.
|
|
92
|
+
WORK=""
|
|
93
|
+
SID=$(json_str "session_id")
|
|
94
|
+
if [ -n "$SID" ]; then
|
|
95
|
+
BRIDGE="${PLASTIC_TMP:-/tmp}/plastic-${SID}.json"
|
|
96
|
+
if [ -f "$BRIDGE" ]; then
|
|
97
|
+
# Bridge JSON is pretty-printed (one field per line). Scope extraction to the
|
|
98
|
+
# "intent" object so a sibling top-level "id"/"name" can never win (the intent
|
|
99
|
+
# object holds only string fields, so the first "}" closes it). grep/sed only.
|
|
100
|
+
INTENT_BLOCK=$(sed -n '/"intent"[[:space:]]*:[[:space:]]*{/,/}/p' "$BRIDGE" 2>/dev/null)
|
|
101
|
+
B_ID=$(printf '%s\n' "$INTENT_BLOCK" | grep -o '"id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 \
|
|
102
|
+
| sed 's/.*"id"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
103
|
+
B_NAME=$(printf '%s\n' "$INTENT_BLOCK" | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 \
|
|
104
|
+
| sed 's/.*"name"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
105
|
+
if [ -n "$B_ID" ] && [ -n "$B_NAME" ]; then
|
|
106
|
+
EMDASH=$'\342\200\224'
|
|
107
|
+
WORK="${B_ID} ${EMDASH} ${B_NAME}"
|
|
45
108
|
fi
|
|
46
109
|
fi
|
|
47
110
|
fi
|
|
48
111
|
|
|
49
|
-
# ---
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
112
|
+
# --- Most-recently-accessed active intent (mirror dashboard: last savepoint ts) ---
|
|
113
|
+
# Fallback only: runs when no session bridge resolved a work-unit above.
|
|
114
|
+
if [ -z "$WORK" ] && [ -f "$INDEX" ]; then
|
|
115
|
+
BEST_TS=""
|
|
116
|
+
while IFS= read -r line; do
|
|
117
|
+
[ -z "$line" ] && continue
|
|
118
|
+
inside=${line#*[}; inside=${inside%%]*} # "{id} - {title}"
|
|
119
|
+
dir=${line#*](store/}; dir=${dir%%/*} # intent directory name
|
|
120
|
+
[ -z "$dir" ] && continue
|
|
121
|
+
sp="$INDEX_DIR/store/$dir/savepoint.md"
|
|
122
|
+
ts=""
|
|
123
|
+
if [ -f "$sp" ]; then
|
|
124
|
+
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)
|
|
125
|
+
fi
|
|
126
|
+
# ISO8601 sorts lexicographically; keep the newest. Empty ts sorts lowest.
|
|
127
|
+
if [ -z "$BEST_TS" ] || { [ -n "$ts" ] && [ "$ts" \> "$BEST_TS" ]; }; then
|
|
128
|
+
BEST_TS="$ts"
|
|
129
|
+
WORK="$inside"
|
|
130
|
+
fi
|
|
131
|
+
done <<EOF
|
|
132
|
+
$(sed -n '/^## Active$/,/^## /{/^- \[/p;}' "$INDEX")
|
|
133
|
+
EOF
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# --- Build the work-unit "[{project}: {id} {title}]" with title truncation ---
|
|
137
|
+
WORK_SEG=""
|
|
138
|
+
if [ -n "$WORK" ]; then
|
|
139
|
+
# Split "{id} - {title}" on the em-dash (U+2014).
|
|
140
|
+
EMDASH=$'\342\200\224'
|
|
141
|
+
id=${WORK%% $EMDASH *}
|
|
142
|
+
title=${WORK#* $EMDASH }
|
|
143
|
+
# Fallback if the em-dash form is absent: split on first space.
|
|
144
|
+
if [ "$id" = "$WORK" ]; then id=${WORK%% *}; title=${WORK#* }; fi
|
|
145
|
+
# Truncate long titles (~40 chars).
|
|
146
|
+
if [ ${#title} -gt 40 ]; then title="$(printf '%.40s' "$title")..."; fi
|
|
147
|
+
WORK_SEG="${C_BRACK}[${RST}${C_PROJ}${PROJ_LABEL}${RST}${C_INTENT}: ${RST}${C_ID}${id}${RST}${C_INTENT} ${title}${RST}${C_BRACK}]${RST}"
|
|
56
148
|
fi
|
|
149
|
+
|
|
150
|
+
# --- Assemble segments in order, joined by " . " ---
|
|
151
|
+
PARTS=""
|
|
152
|
+
add() { [ -z "$1" ] && return; if [ -z "$PARTS" ]; then PARTS="$1"; else PARTS="$PARTS$SEP$1"; fi; }
|
|
153
|
+
|
|
154
|
+
# Order: stable values first (model, work-unit), then the volatile-length
|
|
155
|
+
# values last (version, path) so the line does not visually jump as they change.
|
|
156
|
+
[ -n "$MODEL" ] && add "${C_MODEL}${MODEL}${RST}"
|
|
157
|
+
[ -n "$WORK_SEG" ] && add "$WORK_SEG"
|
|
158
|
+
if [ -n "$VERSION" ]; then
|
|
159
|
+
vseg="${C_VER}Plastic ${VERSION}${RST}"
|
|
160
|
+
[ -n "$NEXT" ] && vseg="${vseg}${C_VER} "$'\342\206\222'" ${RST}${C_NEXT}${NEXT}${RST}"
|
|
161
|
+
add "$vseg"
|
|
162
|
+
fi
|
|
163
|
+
[ -n "$CWD" ] && add "${C_PATH}${CWD}${RST}"
|
|
164
|
+
|
|
165
|
+
printf '%s\n' "$PARTS"
|
package/package.json
CHANGED
package/scripts/agent-report
CHANGED
|
@@ -108,6 +108,26 @@ def outcome_line(intent_dir)
|
|
|
108
108
|
"(no outcome yet)"
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
# The insights line: the LAST line of the intent file's `## Insights` section
|
|
112
|
+
# (the newest nugget, since entries are append-only newest-at-bottom), else
|
|
113
|
+
# `(none)`. Pure read of the intent dir, no clock.
|
|
114
|
+
def insights_line(intent_dir)
|
|
115
|
+
ifile = Bridge.intent_file(intent_dir)
|
|
116
|
+
return "(none)" unless File.exist?(ifile)
|
|
117
|
+
|
|
118
|
+
lines = File.read(ifile).split("\n", -1)
|
|
119
|
+
idx = lines.index { |l| l.strip == "## Insights" }
|
|
120
|
+
return "(none)" if idx.nil?
|
|
121
|
+
|
|
122
|
+
last = nil
|
|
123
|
+
(idx + 1).upto(lines.length - 1) do |i|
|
|
124
|
+
break if lines[i].start_with?("## ")
|
|
125
|
+
|
|
126
|
+
last = lines[i] unless lines[i].strip.empty?
|
|
127
|
+
end
|
|
128
|
+
last || "(none)"
|
|
129
|
+
end
|
|
130
|
+
|
|
111
131
|
intent_dir_arg, role = parse_args(ARGV)
|
|
112
132
|
|
|
113
133
|
if intent_dir_arg.nil? || intent_dir_arg.empty?
|
|
@@ -137,6 +157,7 @@ lines << "Status: synthesized (filesystem-derived; no agent-authored report)"
|
|
|
137
157
|
lines << "Artifacts present: #{artifacts_str}"
|
|
138
158
|
lines << "Checklist: #{checklist_progress(intent_dir)}"
|
|
139
159
|
lines << "Outcome: #{outcome_line(intent_dir)}"
|
|
160
|
+
lines << "Insights: #{insights_line(intent_dir)}"
|
|
140
161
|
lines << "=== end report ==="
|
|
141
162
|
|
|
142
163
|
puts lines.join("\n")
|