@procrastivity/clast 0.0.1 → 0.0.3

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.
@@ -11,7 +11,7 @@ fi
11
11
  _CLAST_LIB_SOURCED=1
12
12
 
13
13
  # Hard dependency check: jq must be on PATH. Exit 3 (env problem) per
14
- # docs/overview.md#exit-codes if missing — no grep/sed fallback.
14
+ # docs/explanation/conventions.md#exit-codes if missing — no grep/sed fallback.
15
15
  if ! command -v jq >/dev/null 2>&1; then
16
16
  echo "clast: error: required dependency 'jq' not found on PATH" >&2
17
17
  exit 3
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Sourced after clast-lib.bash. The manifest is an append-only JSONL log at
4
4
  # $(clast_journal_dir)/.manifest.jsonl, one line per capture event. Per
5
- # docs/cli-contract.md#manifest-line each line has seven required fields:
5
+ # docs/reference/cli.md#manifest-line each line has seven required fields:
6
6
  # session_id, source, snapshot, captured_at, source_mtime, source_size,
7
7
  # day_bucket. Lookups use "most recent line wins" semantics.
8
8
  # shellcheck shell=bash
@@ -68,7 +68,7 @@ clast_manifest_append() {
68
68
  }
69
69
 
70
70
  # Append-only single-line write — crash-safe at line boundary per
71
- # docs/overview.md#cross-machine-considerations. Do NOT use
71
+ # docs/explanation/architecture.md#cross-machine-considerations. Do NOT use
72
72
  # clast_atomic_write here: it rewrites the whole file and would clobber
73
73
  # concurrent appends from another machine.
74
74
  local manifest_path
@@ -3,8 +3,8 @@
3
3
  # Sourced after clast-lib.bash and clast-decode-lib.bash. The registry is
4
4
  # an append-only JSONL log at $(clast_journal_dir)/projects.json (the
5
5
  # ".json" name is historical — semantically it is JSONL). See
6
- # docs/cli-contract.md#registry-line-in-projectsjson for the on-disk
7
- # schema and docs/cli-contract.md#clast-registry for the resolution rules.
6
+ # docs/reference/cli.md#registry-line-in-projectsjson for the on-disk
7
+ # schema and docs/reference/cli.md#clast-registry for the resolution rules.
8
8
  # shellcheck shell=bash
9
9
  # shellcheck source=lib/clast/clast-lib.bash
10
10
  # shellcheck source=lib/clast/clast-decode-lib.bash
@@ -61,13 +61,18 @@ clast_registry_resolve() {
61
61
  local arr
62
62
  arr="$(clast_registry_list_json)"
63
63
 
64
- # Segment input: starts with `-`. Decode (possibly ambiguous) then
65
- # resolve as a path.
64
+ # Segment input: starts with `-`. Try raw segment first (handles
65
+ # segments registered as-is), then decode to filesystem paths.
66
66
  if [[ "$input" == -* ]]; then
67
+ local slug
68
+ slug="$(_clast_registry_lookup_path "$input" "$arr" 2>/dev/null)" || true
69
+ if [[ -n "$slug" ]]; then
70
+ printf '%s\n' "$slug"
71
+ return 0
72
+ fi
67
73
  local -a candidates=()
68
74
  mapfile -t candidates < <(clast_decode_candidates "$input")
69
- local c slug
70
- # Prefer registry-matching candidate over filesystem-resolving one.
75
+ local c
71
76
  for c in "${candidates[@]}"; do
72
77
  slug="$(_clast_registry_lookup_path "$c" "$arr")" || continue
73
78
  if [[ -n "$slug" ]]; then
@@ -126,6 +131,8 @@ clast_registry_add() {
126
131
  fi
127
132
  remote="$2"; remote_explicit=1; shift 2 ;;
128
133
  --remote=*) remote="${1#*=}"; remote_explicit=1; shift ;;
134
+ --)
135
+ shift; break ;;
129
136
  -*)
130
137
  clast_log_error "clast_registry_add: unknown flag '$1'"
131
138
  return 2
@@ -140,6 +147,19 @@ clast_registry_add() {
140
147
  esac
141
148
  done
142
149
 
150
+ # Consume remaining positional after --
151
+ if [[ $# -gt 0 ]]; then
152
+ if [[ -n "$path" ]]; then
153
+ clast_log_error "clast_registry_add: unexpected positional '$1'"
154
+ return 2
155
+ fi
156
+ path="$1"; shift
157
+ fi
158
+ if [[ $# -gt 0 ]]; then
159
+ clast_log_error "clast_registry_add: unexpected positional '$1'"
160
+ return 2
161
+ fi
162
+
143
163
  # Reject empty / whitespace-only paths.
144
164
  local trimmed="${path//[[:space:]]/}"
145
165
  if [[ -z "$trimmed" ]]; then
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Run six sanity checks against the journal and report findings. With
4
4
  # `--fix`, perform the two safe repairs (manifest rebuild from disk,
5
- # orphan-snapshot removal). See docs/cli-contract.md#clast-doctor.
5
+ # orphan-snapshot removal). See docs/reference/cli.md#clast-doctor.
6
6
  # shellcheck shell=bash
7
7
  # shellcheck source=lib/clast/clast-lib.bash
8
8
  # shellcheck source=lib/clast/clast-manifest-lib.bash
@@ -3,7 +3,7 @@
3
3
  # Curated journal entries live at
4
4
  # $(clast_journal_dir)/entries/YYYY-MM-DD-HHMM-<project-slug>-<session-slug>.md
5
5
  # as Markdown files with a YAML frontmatter block. See
6
- # docs/cli-contract.md#clast-entries and docs/cli-contract.md#entry-frontmatter.
6
+ # docs/reference/cli.md#clast-entries and docs/reference/cli.md#entry-frontmatter.
7
7
  # shellcheck shell=bash
8
8
  # shellcheck source=lib/clast/clast-lib.bash
9
9
  # shellcheck source=lib/clast/clast-manifest-lib.bash
@@ -262,24 +262,20 @@ _clast_entries_list() {
262
262
  return 0
263
263
  fi
264
264
 
265
- printf '%-11s %-6s %-17s %-29s %s\n' \
266
- "date" "time" "project" "slug" "tags"
265
+ printf '%-50s %s\n' "entry" "tags"
267
266
 
268
- local n i row r_date r_time r_project r_slug r_tags tags_disp
267
+ local n i row r_path r_basename r_tags tags_disp
269
268
  n="$(jq 'length' <<<"$rows_json")"
270
269
  for (( i = 0; i < n; i++ )); do
271
270
  row="$(jq -c ".[$i]" <<<"$rows_json")"
272
- r_date="$(jq -r '.date // ""' <<<"$row")"
273
- r_time="$(jq -r '.time // ""' <<<"$row")"
274
- r_project="$(jq -r '.project // ""' <<<"$row")"
275
- r_slug="$(jq -r '.session_slug // ""' <<<"$row")"
271
+ r_path="$(jq -r '.path // ""' <<<"$row")"
272
+ r_basename="$(basename "$r_path")"
276
273
  r_tags="$(jq -r '.tags // [] | join(",")' <<<"$row")"
277
274
  tags_disp="$r_tags"
278
275
  if (( ${#tags_disp} > 30 )); then
279
276
  tags_disp="${tags_disp:0:29}…"
280
277
  fi
281
- printf '%-11s %-6s %-17s %-29s %s\n' \
282
- "$r_date" "$r_time" "$r_project" "$r_slug" "$tags_disp"
278
+ printf '%-50s %s\n' "$r_basename" "$tags_disp"
283
279
  done
284
280
  }
285
281
 
@@ -515,6 +511,7 @@ _clast_entries_write() {
515
511
  trimmed="${rt#"${rt%%[![:space:]]*}"}"
516
512
  trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
517
513
  [[ -z "$trimmed" ]] && continue
514
+ trimmed="${trimmed,,}"
518
515
  if ! [[ "$trimmed" =~ ^[a-z0-9][a-z0-9-]{0,31}$ ]]; then
519
516
  _clast_entries_err "write: invalid tag '$trimmed'"; return 2
520
517
  fi
@@ -532,7 +529,7 @@ _clast_entries_write() {
532
529
  local snapshot_rel mtime
533
530
  snapshot_rel="$(jq -r '.snapshot' <<<"$manifest_line")"
534
531
  mtime="$(jq -r '.source_mtime' <<<"$manifest_line")"
535
- : "$mtime" # unused for now; reserved for future best-effort fields
532
+ # curated_source_mtime: stored in frontmatter for stale-curation detection
536
533
 
537
534
  local seg
538
535
  seg="$(awk -F/ 'NR==1{print $3}' <<<"$snapshot_rel")"
@@ -566,16 +563,13 @@ _clast_entries_write() {
566
563
  fi
567
564
 
568
565
  # Best-effort branch from snapshot.
566
+ # Claude Code stores gitBranch (camelCase) on type:"user" lines, not on line 1.
569
567
  local journal_dir snapshot_abs branch=""
570
568
  journal_dir="$(clast_journal_dir)"
571
569
  snapshot_abs="$journal_dir/$snapshot_rel"
572
570
  if [[ -r "$snapshot_abs" ]]; then
573
- branch="$(head -n1 "$snapshot_abs" 2>/dev/null | jq -r '.cwd // .git_branch // empty' 2>/dev/null || true)"
574
- # `.cwd` isn't a branch, but we keep parity with step 07's TODO: leave best-effort placeholder.
575
- # If the value looks like a path, drop it.
576
- case "$branch" in
577
- /*|"") branch="" ;;
578
- esac
571
+ branch="$(grep -m1 '"gitBranch"' "$snapshot_abs" 2>/dev/null \
572
+ | jq -r '.gitBranch // empty' 2>/dev/null || true)"
579
573
  fi
580
574
 
581
575
  local author machine
@@ -650,6 +644,7 @@ _clast_entries_write() {
650
644
  fm+="session_slug: $(_clast_entries_yaml_string "$slug")"$'\n'
651
645
  fm+="snapshot_path: $(_clast_entries_yaml_string "$snapshot_rel")"$'\n'
652
646
  fm+="machine: $(_clast_entries_yaml_string "$machine")"$'\n'
647
+ fm+="curated_source_mtime: $(_clast_entries_yaml_string "$mtime")"$'\n'
653
648
 
654
649
  local composed="---"$'\n'"$fm""---"$'\n\n'"$body"
655
650
 
@@ -1,7 +1,7 @@
1
1
  # clast-subcommands/projects.bash — `clast projects`.
2
2
  #
3
3
  # Read-only view over .manifest.jsonl: list projects (segments) with
4
- # session activity in a date window. See docs/cli-contract.md#clast-projects.
4
+ # session activity in a date window. See docs/reference/cli.md#clast-projects.
5
5
  # shellcheck shell=bash
6
6
  # shellcheck source=lib/clast/clast-lib.bash
7
7
  # shellcheck source=lib/clast/clast-manifest-lib.bash
@@ -24,7 +24,7 @@ Flags:
24
24
  -h, --help Print this usage and exit.
25
25
 
26
26
  DATE accepts ISO (YYYY-MM-DD), `today`, `yesterday`, `last-week`,
27
- `-Nd`, or `-Nw`. See docs/cli-contract.md#date-parsing.
27
+ `-Nd`, or `-Nw`. See docs/reference/cli.md#date-parsing.
28
28
  EOF
29
29
  }
30
30
 
@@ -98,7 +98,7 @@ clast_cmd_projects() {
98
98
  day_filter="$(clast_today)"
99
99
  fi
100
100
 
101
- # `--until` defaults to `today` per docs/cli-contract.md#clast-projects when
101
+ # `--until` defaults to `today` per docs/reference/cli.md#clast-projects when
102
102
  # `--since` is supplied without an explicit upper bound.
103
103
  if [[ -n "$since_date" && -z "$until_date" ]]; then
104
104
  until_date="$(clast_today)"
@@ -2,29 +2,35 @@
2
2
  #
3
3
  # Read-only view over .manifest.jsonl: list sessions in a date window,
4
4
  # optionally filtered by registry slug. See
5
- # docs/cli-contract.md#clast-sessions.
5
+ # docs/reference/cli.md#clast-sessions.
6
6
  # shellcheck shell=bash
7
7
  # shellcheck source=lib/clast/clast-lib.bash
8
8
  # shellcheck source=lib/clast/clast-manifest-lib.bash
9
9
  # shellcheck source=lib/clast/clast-registry-lib.bash
10
10
  # shellcheck source=lib/clast/clast-decode-lib.bash
11
+ # shellcheck source=lib/clast/clast-dismissed-lib.bash
11
12
 
12
13
  _clast_sessions_usage() {
13
14
  cat <<'EOF'
14
15
  Usage: clast sessions [--day DATE] [--since DATE] [--until DATE] [--project SLUG]
16
+ clast sessions dismiss <session-id> [<session-id>...] [--reason TEXT]
15
17
 
16
18
  List sessions captured in a date window.
17
19
 
20
+ Subcommands:
21
+ dismiss ID... Mark session(s) as dismissed (excluded from future queries).
22
+
18
23
  Flags:
19
- --day DATE Single-day window (default: today). Mutually exclusive
20
- with --since/--until.
21
- --since DATE Start of range (inclusive).
22
- --until DATE End of range (inclusive).
23
- --project SLUG Filter to a single registry slug.
24
- -h, --help Print this usage and exit.
24
+ --day DATE Single-day window (default: today). Mutually exclusive
25
+ with --since/--until.
26
+ --since DATE Start of range (inclusive).
27
+ --until DATE End of range (inclusive).
28
+ --project SLUG Filter to a single registry slug.
29
+ --include-dismissed Include dismissed sessions in results.
30
+ -h, --help Print this usage and exit.
25
31
 
26
32
  DATE accepts ISO (YYYY-MM-DD), `today`, `yesterday`, `last-week`,
27
- `-Nd`, or `-Nw`. See docs/cli-contract.md#date-parsing.
33
+ `-Nd`, or `-Nw`. See docs/reference/cli.md#date-parsing.
28
34
  EOF
29
35
  }
30
36
 
@@ -37,9 +43,72 @@ _clast_sessions_err() {
37
43
  fi
38
44
  }
39
45
 
46
+ # _clast_sessions_dismiss <id> [<id>...] [--reason TEXT]
47
+ _clast_sessions_dismiss() {
48
+ if [[ $# -eq 0 ]]; then
49
+ _clast_sessions_err "dismiss requires at least one session ID"
50
+ return 2
51
+ fi
52
+
53
+ source "$CLAST_LIB/clast-dismissed-lib.bash"
54
+
55
+ local -a ids=()
56
+ local reason=""
57
+ while [[ $# -gt 0 ]]; do
58
+ case "$1" in
59
+ --reason)
60
+ if [[ $# -lt 2 ]]; then _clast_sessions_err "--reason requires a value"; return 2; fi
61
+ reason="$2"; shift 2 ;;
62
+ --reason=*)
63
+ reason="${1#*=}"; shift ;;
64
+ -h|--help)
65
+ _clast_sessions_usage; return 0 ;;
66
+ -*)
67
+ _clast_sessions_err "dismiss: unknown flag '$1'"; return 2 ;;
68
+ *)
69
+ ids+=("$1"); shift ;;
70
+ esac
71
+ done
72
+
73
+ if [[ ${#ids[@]} -eq 0 ]]; then
74
+ _clast_sessions_err "dismiss requires at least one session ID"
75
+ return 2
76
+ fi
77
+
78
+ local id count=0
79
+ for id in "${ids[@]}"; do
80
+ if ! [[ "$id" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then
81
+ _clast_sessions_err "dismiss: '$id' is not a valid UUID"
82
+ return 2
83
+ fi
84
+ if clast_dismissed_check "$id"; then
85
+ if [[ -z "${CLAST_QUIET:-}" ]]; then
86
+ clast_log_info "already dismissed: $id"
87
+ fi
88
+ continue
89
+ fi
90
+ clast_dismissed_add "$id" "$reason"
91
+ count=$(( count + 1 ))
92
+ done
93
+
94
+ if [[ -n "${CLAST_JSON:-}" ]]; then
95
+ jq -cn --argjson count "$count" '{dismissed: $count}'
96
+ elif [[ -z "${CLAST_QUIET:-}" ]]; then
97
+ clast_log_info "Dismissed $count session(s)."
98
+ fi
99
+ }
100
+
40
101
  clast_cmd_sessions() {
102
+ # Handle "sessions dismiss" sub-action before flag parsing.
103
+ if [[ "${1:-}" == "dismiss" ]]; then
104
+ shift
105
+ _clast_sessions_dismiss "$@"
106
+ return $?
107
+ fi
108
+
41
109
  local day_filter="" since_date="" until_date=""
42
110
  local project_filter=""
111
+ local include_dismissed=0
43
112
 
44
113
  while [[ $# -gt 0 ]]; do
45
114
  case "$1" in
@@ -81,6 +150,10 @@ clast_cmd_sessions() {
81
150
  project_filter="$2"; shift 2 ;;
82
151
  --project=*)
83
152
  project_filter="${1#*=}"; shift ;;
153
+ --include-dismissed)
154
+ include_dismissed=1; shift ;;
155
+ --json)
156
+ export CLAST_JSON=1; shift ;;
84
157
  -h|--help)
85
158
  _clast_sessions_usage; return 0 ;;
86
159
  --)
@@ -101,7 +174,7 @@ clast_cmd_sessions() {
101
174
  day_filter="$(clast_today)"
102
175
  fi
103
176
 
104
- # `--until` defaults to `today` per docs/cli-contract.md#clast-sessions
177
+ # `--until` defaults to `today` per docs/reference/cli.md#clast-sessions
105
178
  # when `--since` is supplied without an explicit upper bound.
106
179
  if [[ -n "$since_date" && -z "$until_date" ]]; then
107
180
  until_date="$(clast_today)"
@@ -113,6 +186,13 @@ clast_cmd_sessions() {
113
186
  local journal_dir
114
187
  journal_dir="$(clast_journal_dir)"
115
188
 
189
+ # Build dismissed set unless --include-dismissed was passed.
190
+ source "$CLAST_LIB/clast-dismissed-lib.bash"
191
+ declare -A dismissed_ids=()
192
+ if (( include_dismissed == 0 )); then
193
+ clast_dismissed_set dismissed_ids
194
+ fi
195
+
116
196
  # Build the project segment whitelist if --project was passed.
117
197
  declare -A allowed_segs=()
118
198
  local have_project_filter=0
@@ -134,6 +214,10 @@ clast_cmd_sessions() {
134
214
  [[ -z "$line" ]] && continue
135
215
  sid="$(jq -r '.session_id' <<<"$line")"
136
216
  [[ -z "$sid" || "$sid" == "null" ]] && continue
217
+ # Skip dismissed sessions early to avoid unnecessary work.
218
+ if [[ -n "${dismissed_ids[$sid]:-}" ]]; then
219
+ continue
220
+ fi
137
221
  latest_line["$sid"]="$line"
138
222
  done < <(clast_manifest_iterate "$filter")
139
223
 
@@ -183,12 +267,33 @@ clast_cmd_sessions() {
183
267
  branch=""
184
268
 
185
269
  curated=false
270
+ local stale=false
186
271
  if [[ -d "$entries_dir" ]]; then
187
- if grep -l "session_id: $sid" "$entries_dir"/*.md 2>/dev/null | head -n1 | grep -q .; then
272
+ local entry_matches
273
+ entry_matches="$(grep -l "session_id: $sid" "$entries_dir"/*.md 2>/dev/null)" || true
274
+ if [[ -n "$entry_matches" ]]; then
188
275
  curated=true
276
+ local best_curated_mtime="" ef cm
277
+ while IFS= read -r ef; do
278
+ [[ -z "$ef" ]] && continue
279
+ cm="$(grep '^curated_source_mtime:' "$ef" 2>/dev/null | head -n1 | sed 's/^curated_source_mtime:[[:space:]]*//')" || true
280
+ cm="${cm#\"}"
281
+ cm="${cm%\"}"
282
+ if [[ -n "$cm" && ( -z "$best_curated_mtime" || "$cm" > "$best_curated_mtime" ) ]]; then
283
+ best_curated_mtime="$cm"
284
+ fi
285
+ done <<<"$entry_matches"
286
+ if [[ -n "$best_curated_mtime" && "$best_curated_mtime" != "$mtime" ]]; then
287
+ stale=true
288
+ fi
189
289
  fi
190
290
  fi
191
291
 
292
+ local is_dismissed=false
293
+ if [[ -n "${dismissed_ids[$sid]:-}" ]]; then
294
+ is_dismissed=true
295
+ fi
296
+
192
297
  rows+=("$(jq -cn \
193
298
  --arg session_id "$sid" \
194
299
  --arg project "$slug" \
@@ -200,6 +305,8 @@ clast_cmd_sessions() {
200
305
  --arg snapshot_path "$snapshot" \
201
306
  --arg day_bucket "$day_bucket" \
202
307
  --argjson curated "$curated" \
308
+ --argjson stale "$stale" \
309
+ --argjson dismissed "$is_dismissed" \
203
310
  '{
204
311
  session_id: $session_id,
205
312
  project: $project,
@@ -210,7 +317,9 @@ clast_cmd_sessions() {
210
317
  msg_count_approx: $msg_count_approx,
211
318
  snapshot_path: $snapshot_path,
212
319
  day_bucket: $day_bucket,
213
- curated: $curated
320
+ curated: $curated,
321
+ stale: $stale,
322
+ dismissed: $dismissed
214
323
  }')")
215
324
  done
216
325
 
@@ -1,7 +1,7 @@
1
1
  # clast-subcommands/show.bash — `clast show <session-id>`.
2
2
  #
3
3
  # Dump metadata + (optionally) first/last turns of a single captured
4
- # session. See docs/cli-contract.md#clast-show.
4
+ # session. See docs/reference/cli.md#clast-show.
5
5
  # shellcheck shell=bash
6
6
  # shellcheck source=lib/clast/clast-lib.bash
7
7
  # shellcheck source=lib/clast/clast-manifest-lib.bash
@@ -224,7 +224,7 @@ clast_cmd_show() {
224
224
  _clast_show_user_messages() {
225
225
  local path="$1"
226
226
  jq -r '
227
- select((.role // .message.role) == "user")
227
+ select((.role // .message.role // .type) == "user")
228
228
  | (.message.content // .content // empty)
229
229
  | if type == "array" then map(.text? // "") | join(" ") else . end
230
230
  | select(. != null and . != "")
@@ -237,9 +237,9 @@ _clast_show_collect_turns() {
237
237
  local path="$1"
238
238
  jq -sc '
239
239
  map(
240
- select((.role // .message.role) as $r | $r == "user" or $r == "assistant")
240
+ select((.role // .message.role // .type) as $r | $r == "user" or $r == "assistant")
241
241
  | {
242
- role: (.role // .message.role),
242
+ role: (.role // .message.role // .type),
243
243
  text: ((.message.content // .content // "") | if type == "array" then map(.text? // "") | join(" ") else . end)
244
244
  }
245
245
  | select(.text != null and .text != "")
@@ -28,7 +28,7 @@ EOF
28
28
 
29
29
  # _clast_snapshot_bucket_for_epoch <epoch>
30
30
  # Mirror of clast_today's cutoff math against an arbitrary epoch. Local
31
- # time per docs/overview.md#conventions.
31
+ # time per docs/explanation/conventions.md.
32
32
  _clast_snapshot_bucket_for_epoch() {
33
33
  local epoch="$1"
34
34
  local cutoff="${CLAST_DAY_CUTOFF:-04:00}"
@@ -247,7 +247,7 @@ _clast_snapshot_emit_summary() {
247
247
  fi
248
248
 
249
249
  # Silent no-op: load-bearing for the SessionStart hook (step 11). Re-read
250
- # docs/cli-contract.md#clast-snapshot before changing this.
250
+ # docs/reference/cli.md#clast-snapshot before changing this.
251
251
  if (( ${#_caps[@]} == 0 && ${#_errs[@]} == 0 )); then
252
252
  return 0
253
253
  fi
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Read-only summary of journal activity over a date window using manifest
4
4
  # + filesystem stat only. No JSONL body parsing. See
5
- # docs/cli-contract.md#clast-stats.
5
+ # docs/reference/cli.md#clast-stats.
6
6
  # shellcheck shell=bash
7
7
  # shellcheck source=lib/clast/clast-lib.bash
8
8
  # shellcheck source=lib/clast/clast-manifest-lib.bash
@@ -23,7 +23,7 @@ Flags:
23
23
  -h, --help Print this usage and exit.
24
24
 
25
25
  DATE accepts ISO (YYYY-MM-DD), `today`, `yesterday`, `last-week`,
26
- `-Nd`, or `-Nw`. See docs/cli-contract.md#date-parsing.
26
+ `-Nd`, or `-Nw`. See docs/reference/cli.md#date-parsing.
27
27
  EOF
28
28
  }
29
29
 
@@ -0,0 +1,31 @@
1
+ You are synthesizing a project briefing for the user. They are about to start work on a project and want a tight summary of where they left off.
2
+
3
+ Produce a briefing using this structure (omit any section that has no content):
4
+
5
+ ## Wakeup briefing — {project}
6
+
7
+ **Active thread:** one-line from most recent entry's "Open threads", or "None"
8
+
9
+ **Last session:** date, branch, one-line goal
10
+ - Work done: 2-3 bullets condensed from most recent entry
11
+ - Open threads: bullets, if any
12
+ - Dead ends to avoid: bullets, if any
13
+
14
+ **Recent sessions:** (up to 5 entries)
15
+ - date [branch] slug: one-line goal
16
+
17
+ **Today's breadcrumbs:** (if any)
18
+ - HH:MM — text
19
+
20
+ **Today's sessions:** (if user has already worked today)
21
+ - HH:MM start: branch, msg-count messages
22
+
23
+ **Suggested next step:** derived from active thread + breadcrumbs
24
+
25
+ Be concise. Use the user's terminology. Don't repeat content across sections. The total briefing should be 2-5k tokens — if you're approaching that, summarize rather than list verbatim.
26
+
27
+ For the "Suggested next step": prefer the most recent entry's "Open threads" content, then the most recent breadcrumb, then a synthesis of the recent work. If nothing concrete, say "No active thread."
28
+
29
+ End with one of:
30
+ - "Resume? Active thread: '<thread>'. Suggested next step: <step>."
31
+ - "No active thread. Last session ended cleanly. What are you working on today?"
@@ -0,0 +1,10 @@
1
+ Project: {{project}}
2
+
3
+ Recent curated entries (newest first):
4
+ {{entries}}
5
+
6
+ Today's breadcrumbs for this project:
7
+ {{breadcrumbs}}
8
+
9
+ Today's session activity for this project:
10
+ {{sessions}}
@@ -0,0 +1,29 @@
1
+ You are drafting a journal entry for a Claude Code session that the user just reviewed. The entry will be written to the user's journal and may be read days or weeks later to refresh context on what was happening.
2
+
3
+ Draft a journal entry in this exact markdown structure. Omit any section that has no content (do not write "N/A"):
4
+
5
+ # Session: <short human-readable title>
6
+
7
+ ## Goal
8
+ One sentence describing what this session was trying to accomplish.
9
+
10
+ ## What shipped
11
+ - Bullet list of what actually got done (files written, features built, fixes landed). Extract from the transcript.
12
+
13
+ ## Issues + fixes
14
+ - **Issue:** what broke. **Fix:** what resolved it.
15
+
16
+ ## Dead ends touched
17
+ - **Tried:** approach.
18
+ - Note: if you cannot tell *why* an approach was abandoned from the transcript, leave that for the user to fill in. Do not speculate.
19
+
20
+ ## Open threads
21
+ - Anything still unfinished or deferred. Use the breadcrumbs and the last turns of the session as signal.
22
+
23
+ ## Notes
24
+ - Anything else useful for the next session in this project.
25
+
26
+ Be concise. Prefer bullets over paragraphs. Use the user's terminology (project-specific names, file paths). Do not invent details. If you are uncertain about something, omit it rather than guess.
27
+
28
+ After the entry, add a blank line and then: "Suggested tags: tag1, tag2, tag3"
29
+ Tags must be lowercase kebab-case (regex: `^[a-z0-9][a-z0-9-]{0,31}$`). Examples: `adrs`, `symfony-bot`, `mr-umbrella`, `phase-0`. Never use uppercase letters.
@@ -0,0 +1,15 @@
1
+ Session metadata:
2
+ - Project: {{project}}
3
+ - Branch: {{branch}}
4
+ - Start: {{start}}
5
+ - End: {{end}}
6
+ - Approximate messages: {{msg_count}}
7
+
8
+ First turns of the session:
9
+ {{first_turns}}
10
+
11
+ Last turns of the session:
12
+ {{last_turns}}
13
+
14
+ Breadcrumbs the user left during this session's day:
15
+ {{breadcrumbs}}
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@procrastivity/clast",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
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": {
7
- "clast": "bin/clast"
7
+ "clast": "bin/clast",
8
+ "clast-wake": "bin/clast-wake",
9
+ "clast-brief": "bin/clast-brief"
8
10
  },
9
11
  "files": [
10
12
  "bin/",