@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] [--window work-days|file-dates]
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: corpus start.
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. Cache hit (matching fingerprint) reuses; miss
62
- # calls the LLM and writes the cache. Returns nonzero on LLM failure.
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
- elif ! summary="$(_clast_retrosum_summary "$project" "$work_day" "$cache_id" "$body" "$cache_dir" "$refresh")"; then
159
- clast_porcelain_warn "summary failed for session ${sid:-<no id>} leaving it unsummarized"
160
- summary="(summary unavailable)"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@procrastivity/clast",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Capture, curate, and surface Claude Code session history across all your projects.",
5
5
  "homepage": "https://github.com/procrastivity/clast#readme",
6
6
  "bin": {