@procrastivity/clast 0.0.5 → 0.0.6
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.
|
@@ -9,26 +9,58 @@
|
|
|
9
9
|
|
|
10
10
|
_clast_retrosum_usage() {
|
|
11
11
|
cat <<'EOF'
|
|
12
|
-
Usage: clast retro [--from DATE] [--to DATE] [--
|
|
13
|
-
[--refresh] [--json]
|
|
12
|
+
Usage: clast retro [--from DATE] [--to DATE] [--all]
|
|
13
|
+
[--window work-days|file-dates] [--refresh] [--json]
|
|
14
14
|
|
|
15
15
|
Condense the work retrospective (grouped by actual work day → project) into
|
|
16
16
|
model-written bullets per session. Structure is deterministic; only the prose
|
|
17
17
|
condensation calls the LLM. Summaries are cached per session under
|
|
18
18
|
<journal>/.retro-summaries/ and reused until the session content changes.
|
|
19
19
|
|
|
20
|
+
With no window flags, defaults to the last 7 days (one model call per new
|
|
21
|
+
session, so an unbounded run over the whole corpus can be slow and costly).
|
|
22
|
+
|
|
20
23
|
Flags:
|
|
21
|
-
--from DATE Start of the window (inclusive). Default:
|
|
24
|
+
--from DATE Start of the window (inclusive). Default: 7 days ago.
|
|
22
25
|
--to DATE End of the window (inclusive). Default: corpus end.
|
|
26
|
+
--all Cover the whole corpus (overrides the 7-day default).
|
|
23
27
|
--window WHICH work-days (default) | file-dates. See `clast-plumbing retro`.
|
|
24
28
|
--refresh Ignore cached summaries and re-summarize (rewrites the cache).
|
|
25
29
|
--json Emit the manifest with a `summary` per session (no render).
|
|
26
30
|
-h, --help Print this usage and exit.
|
|
27
31
|
|
|
32
|
+
DATE accepts ISO (YYYY-MM-DD), `today`, `yesterday`, `last-week`, `-Nd`, `-Nw`.
|
|
33
|
+
|
|
28
34
|
Requires the CLAST_LLM_* env vars (see `clast --help`).
|
|
29
35
|
EOF
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
# _clast_retro_progress <msg>
|
|
39
|
+
# Emit a progress line to stderr so stdout (render or --json) stays clean.
|
|
40
|
+
# Silent when CLAST_QUIET is set or stderr is not a TTY; CLAST_RETRO_PROGRESS=always
|
|
41
|
+
# forces it on (used by tests, which have no tty).
|
|
42
|
+
_clast_retro_progress() {
|
|
43
|
+
[[ -n "${CLAST_QUIET:-}" ]] && return 0
|
|
44
|
+
if [[ "${CLAST_RETRO_PROGRESS:-}" == "always" || -t 2 ]]; then
|
|
45
|
+
printf 'clast: %s\n' "$*" >&2
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# _clast_retro_now — epoch seconds with fraction (GNU date). Falls back to whole
|
|
50
|
+
# seconds where `%N` is unsupported (e.g. BSD/macOS date echoes a literal N).
|
|
51
|
+
_clast_retro_now() {
|
|
52
|
+
local t
|
|
53
|
+
t="$(date +%s.%N 2>/dev/null)" || t="$(date +%s)"
|
|
54
|
+
[[ "$t" == *N* ]] && t="$(date +%s)"
|
|
55
|
+
printf '%s' "$t"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# _clast_retro_elapsed <start> <end> — seconds between two _clast_retro_now
|
|
59
|
+
# readings, one decimal place (never negative).
|
|
60
|
+
_clast_retro_elapsed() {
|
|
61
|
+
awk -v a="$1" -v b="$2" 'BEGIN { d = b - a; if (d < 0) d = 0; printf "%.1f", d }'
|
|
62
|
+
}
|
|
63
|
+
|
|
32
64
|
# _clast_retrosum_fingerprint (stdin → short hex/cksum on stdout)
|
|
33
65
|
# Content fingerprint of a session body; changes invalidate the cache.
|
|
34
66
|
_clast_retrosum_fingerprint() {
|
|
@@ -58,8 +90,11 @@ _clast_retrosum_build_user() {
|
|
|
58
90
|
}
|
|
59
91
|
|
|
60
92
|
# _clast_retrosum_summary <project> <work_day> <session_id> <body> <cache_dir> <refresh>
|
|
61
|
-
# Echo the condensed summary.
|
|
62
|
-
#
|
|
93
|
+
# Echo the condensed summary. Return code tells the caller what happened so it
|
|
94
|
+
# can time only the calls that hit the model:
|
|
95
|
+
# 0 — served from cache (no model call)
|
|
96
|
+
# 3 — fresh summary via a model call (success)
|
|
97
|
+
# 1 — model call failed
|
|
63
98
|
_clast_retrosum_summary() {
|
|
64
99
|
local project="$1" work_day="$2" sid="$3" body="$4" cache_dir="$5" refresh="$6"
|
|
65
100
|
local fp cache_file cached_fp
|
|
@@ -74,6 +109,7 @@ _clast_retrosum_summary() {
|
|
|
74
109
|
fi
|
|
75
110
|
fi
|
|
76
111
|
|
|
112
|
+
_clast_retro_progress " querying model..."
|
|
77
113
|
local system user summary
|
|
78
114
|
system="$(clast_porcelain_load_system_prompt retro-summary-system)"
|
|
79
115
|
user="$(_clast_retrosum_build_user "$project" "$work_day" "$sid" "$body")"
|
|
@@ -86,6 +122,7 @@ _clast_retrosum_summary() {
|
|
|
86
122
|
>"$cache_file" 2>/dev/null || clast_porcelain_warn "failed to cache summary for $sid"
|
|
87
123
|
fi
|
|
88
124
|
printf '%s' "$summary"
|
|
125
|
+
return 3
|
|
89
126
|
}
|
|
90
127
|
|
|
91
128
|
# _clast_retrosum_journal_dir — resolve the journal dir (for the cache).
|
|
@@ -100,7 +137,7 @@ _clast_retrosum_journal_dir() {
|
|
|
100
137
|
}
|
|
101
138
|
|
|
102
139
|
clast_cmd_retro() {
|
|
103
|
-
local from="" to="" window="work-days" refresh=0 as_json=0
|
|
140
|
+
local from="" to="" window="work-days" refresh=0 as_json=0 all=0
|
|
104
141
|
|
|
105
142
|
while [[ $# -gt 0 ]]; do
|
|
106
143
|
case "$1" in
|
|
@@ -108,6 +145,7 @@ clast_cmd_retro() {
|
|
|
108
145
|
--from=*) from="${1#*=}"; shift ;;
|
|
109
146
|
--to) [[ $# -lt 2 ]] && { clast_porcelain_log_error "retro: --to requires a value"; return 2; }; to="$2"; shift 2 ;;
|
|
110
147
|
--to=*) to="${1#*=}"; shift ;;
|
|
148
|
+
--all) all=1; shift ;;
|
|
111
149
|
--window) [[ $# -lt 2 ]] && { clast_porcelain_log_error "retro: --window requires a value"; return 2; }; window="$2"; shift 2 ;;
|
|
112
150
|
--window=*) window="${1#*=}"; shift ;;
|
|
113
151
|
--refresh) refresh=1; shift ;;
|
|
@@ -118,9 +156,17 @@ clast_cmd_retro() {
|
|
|
118
156
|
esac
|
|
119
157
|
done
|
|
120
158
|
|
|
159
|
+
# An unbounded retro means one model call per session across the whole corpus
|
|
160
|
+
# (slow and costly). Default to the last 7 days unless the caller bounded the
|
|
161
|
+
# window themselves (--from/--to) or opted into everything with --all.
|
|
162
|
+
if (( ! all )) && [[ -z "$from" && -z "$to" ]]; then
|
|
163
|
+
from="-7d"
|
|
164
|
+
fi
|
|
165
|
+
|
|
121
166
|
clast_porcelain_preflight_llm
|
|
122
167
|
|
|
123
168
|
# Structure + per-session bodies from the deterministic core.
|
|
169
|
+
_clast_retro_progress "building work-day manifest..."
|
|
124
170
|
local -a pl=(--json retro --bodies --window "$window")
|
|
125
171
|
[[ -n "$from" ]] && pl+=(--from "$from")
|
|
126
172
|
[[ -n "$to" ]] && pl+=(--to "$to")
|
|
@@ -131,12 +177,26 @@ clast_cmd_retro() {
|
|
|
131
177
|
clast_porcelain_die "retro: ${msg:-failed to build manifest}" 2
|
|
132
178
|
fi
|
|
133
179
|
|
|
180
|
+
# Report the resolved window + discovered work days before the slow summarize
|
|
181
|
+
# loop — the "dates it resolved to" the user wants to see up front.
|
|
182
|
+
local pf pt pw ndays nsess daylist
|
|
183
|
+
pf="$(jq -r '.from // "(start)"' <<<"$manifest")"
|
|
184
|
+
pt="$(jq -r '.to // "(end)"' <<<"$manifest")"
|
|
185
|
+
pw="$(jq -r '.window' <<<"$manifest")"
|
|
186
|
+
ndays="$(jq '.days | length' <<<"$manifest")"
|
|
187
|
+
nsess="$(jq '[.days[].projects[].sessions[]] | length' <<<"$manifest")"
|
|
188
|
+
daylist="$(jq -r '[.days[].day] | join(", ")' <<<"$manifest")"
|
|
189
|
+
_clast_retro_progress "window: $pf -> $pt ($pw)"
|
|
190
|
+
_clast_retro_progress "resolved $nsess session(s) across $ndays day(s): $daylist"
|
|
191
|
+
|
|
134
192
|
local cache_dir
|
|
135
193
|
cache_dir="$(_clast_retrosum_journal_dir)/.retro-summaries"
|
|
136
194
|
|
|
137
195
|
# Summarize each session; collect key → summary.
|
|
138
196
|
local -a summary_pairs=()
|
|
139
|
-
local sess sid key cache_id project work_day body title summary
|
|
197
|
+
local sess sid key cache_id project work_day body title summary pname
|
|
198
|
+
local t0 t1 dt rc
|
|
199
|
+
local idx=0 total="$nsess" queried=0 model_total="0.0"
|
|
140
200
|
while IFS= read -r sess; do
|
|
141
201
|
[[ -z "$sess" ]] && continue
|
|
142
202
|
sid="$(jq -r '.session_id // ""' <<<"$sess")"
|
|
@@ -153,14 +213,35 @@ clast_cmd_retro() {
|
|
|
153
213
|
work_day="$(jq -r '.work_day' <<<"$sess")"
|
|
154
214
|
body="$(jq -r '.body // ""' <<<"$sess")"
|
|
155
215
|
project="$(jq -r '.project_path // "(no project)"' <<<"$sess")"
|
|
216
|
+
idx=$(( idx + 1 ))
|
|
217
|
+
pname="$(jq -r '.progress_project // .project_path // "(no project)"' <<<"$sess")"
|
|
218
|
+
title="$(jq -r '.title // ""' <<<"$sess")"
|
|
219
|
+
_clast_retro_progress "[$idx/$total] $work_day $pname ${title:-${sid:0:8}}"
|
|
156
220
|
if [[ -z "$body" ]]; then
|
|
157
221
|
summary="(no body to summarize)"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
222
|
+
else
|
|
223
|
+
# Time the call so we can report how long the model took. rc distinguishes
|
|
224
|
+
# a cache hit (0, instant) from a fresh model call (3) from a failure.
|
|
225
|
+
rc=0
|
|
226
|
+
t0="$(_clast_retro_now)"
|
|
227
|
+
summary="$(_clast_retrosum_summary "$project" "$work_day" "$cache_id" "$body" "$cache_dir" "$refresh")" || rc=$?
|
|
228
|
+
t1="$(_clast_retro_now)"
|
|
229
|
+
case "$rc" in
|
|
230
|
+
0) : ;; # served from cache — no model call, nothing to time
|
|
231
|
+
3) dt="$(_clast_retro_elapsed "$t0" "$t1")"
|
|
232
|
+
queried=$(( queried + 1 ))
|
|
233
|
+
model_total="$(awk -v a="$model_total" -v d="$dt" 'BEGIN { printf "%.1f", a + d }')"
|
|
234
|
+
_clast_retro_progress " done in ${dt}s (model total ${model_total}s)" ;;
|
|
235
|
+
*) clast_porcelain_warn "summary failed for session ${sid:-<no id>} — leaving it unsummarized"
|
|
236
|
+
summary="(summary unavailable)" ;;
|
|
237
|
+
esac
|
|
161
238
|
fi
|
|
162
239
|
summary_pairs+=("$(jq -cn --arg k "$key" --arg v "$summary" '{key:$k, summary:$v}')")
|
|
163
|
-
done < <(jq -c '.days[].projects[].sessions[]' <<<"$manifest")
|
|
240
|
+
done < <(jq -c '.days[].projects[] | .project_name as $pn | .sessions[] | . + {progress_project: $pn}' <<<"$manifest")
|
|
241
|
+
|
|
242
|
+
if (( total > 0 )); then
|
|
243
|
+
_clast_retro_progress "summarized $total session(s); $queried queried the model (${model_total}s)"
|
|
244
|
+
fi
|
|
164
245
|
|
|
165
246
|
# Fold summaries back into the manifest and drop the raw bodies. The manifest
|
|
166
247
|
# carries --bodies only as summarizer input; neither the JSON nor the render
|
package/package.json
CHANGED