halfcycle 0.4.12 → 0.4.14

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 (54) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/bin.bundle.mjs +9366 -567
  3. package/bin/pre-tool-use-filter.sh +470 -0
  4. package/dist/bin.d.ts +1 -0
  5. package/dist/bin.d.ts.map +1 -1
  6. package/dist/build-record/pair-io.d.ts +49 -0
  7. package/dist/build-record/pair-io.d.ts.map +1 -0
  8. package/dist/build-record/render-change.d.ts +25 -0
  9. package/dist/build-record/render-change.d.ts.map +1 -0
  10. package/dist/build-record/render-v2.d.ts +10 -5
  11. package/dist/build-record/render-v2.d.ts.map +1 -1
  12. package/dist/build-record/write-change.d.ts +63 -0
  13. package/dist/build-record/write-change.d.ts.map +1 -0
  14. package/dist/build-record/write.d.ts +5 -15
  15. package/dist/build-record/write.d.ts.map +1 -1
  16. package/dist/change.d.ts +136 -0
  17. package/dist/change.d.ts.map +1 -0
  18. package/dist/check-tasks.d.ts +71 -0
  19. package/dist/check-tasks.d.ts.map +1 -0
  20. package/dist/claude-plan.d.ts +114 -0
  21. package/dist/claude-plan.d.ts.map +1 -0
  22. package/dist/cli-contract.d.ts +12 -1
  23. package/dist/cli-contract.d.ts.map +1 -1
  24. package/dist/cli.js +9017 -5228
  25. package/dist/cli.js.map +3 -3
  26. package/dist/close-phase.d.ts +113 -10
  27. package/dist/close-phase.d.ts.map +1 -1
  28. package/dist/halfcycle-monorepo.d.ts +26 -0
  29. package/dist/halfcycle-monorepo.d.ts.map +1 -0
  30. package/dist/index.js +1617 -845
  31. package/dist/index.js.map +3 -3
  32. package/dist/install-manifest.d.ts +18 -1
  33. package/dist/install-manifest.d.ts.map +1 -1
  34. package/dist/install.d.ts +225 -3
  35. package/dist/install.d.ts.map +1 -1
  36. package/dist/merge-settings.d.ts +25 -2
  37. package/dist/merge-settings.d.ts.map +1 -1
  38. package/dist/open-phase.d.ts +1 -1
  39. package/dist/open-phase.d.ts.map +1 -1
  40. package/dist/pace.d.ts +138 -0
  41. package/dist/pace.d.ts.map +1 -0
  42. package/dist/phase-work.d.ts +37 -0
  43. package/dist/phase-work.d.ts.map +1 -0
  44. package/dist/plan.d.ts +5 -2
  45. package/dist/plan.d.ts.map +1 -1
  46. package/dist/prove.d.ts +77 -0
  47. package/dist/prove.d.ts.map +1 -0
  48. package/dist/review.d.ts +19 -1
  49. package/dist/review.d.ts.map +1 -1
  50. package/dist/system-report.d.ts +4 -4
  51. package/dist/system-report.d.ts.map +1 -1
  52. package/dist/uninstall.d.ts +4 -1
  53. package/dist/uninstall.d.ts.map +1 -1
  54. package/package.json +1 -1
@@ -0,0 +1,470 @@
1
+ # --- Halfcycle cheap pass before a tool runs (generated; do not edit) ---
2
+ # Before each tool call: when the call is one Halfcycle's check could not ask
3
+ # about, answer here, write the call's time line, and do not start the check.
4
+ # Generated from the check's own list of risky spellings; anything it is not
5
+ # sure of goes on to the check.
6
+ halfcycle_pt_skip() {
7
+ local hc_t=${EPOCHREALTIME:-} hc_status
8
+ [ "${BASH_VERSINFO[0]:-0}" -ge 5 ] || return 1
9
+ case $hc_t in [0-9]*[.,][0-9][0-9][0-9]*) ;; *) return 1 ;; esac
10
+ hc_pt_live_text= hc_pt_live_sid= hc_pt_live_agent= hc_pt_live_dir=
11
+ halfcycle_pt_quiet || return 1
12
+ halfcycle_pt_live || return 1
13
+ halfcycle_pt_own_copy || return 1
14
+ halfcycle_pt_time_line "$hc_t"
15
+ }
16
+ halfcycle_pt_live() {
17
+ local LC_ALL=C hc_val hc_sid hc_agent= hc_cwd hc_dir hc_file hc_text= hc_ng=0 hc_re='^[A-Za-z0-9_-]{1,128}$'
18
+ local -a hc_files
19
+ halfcycle_pt_string session_id
20
+ case $? in 0) hc_sid=$hc_val ;; 1) return 0 ;; *) return 1 ;; esac
21
+ [[ $hc_sid =~ $hc_re ]] || return 0
22
+ halfcycle_pt_string agent_id
23
+ case $? in 0) hc_agent=$hc_val ;; 1) ;; *) return 1 ;; esac
24
+ if [ -n "$hc_agent" ]; then
25
+ [[ $hc_agent =~ $hc_re ]] || return 0
26
+ hc_agent=-$hc_agent
27
+ fi
28
+ halfcycle_pt_string cwd
29
+ case $? in 0) hc_cwd=$hc_val ;; 1) return 0 ;; *) return 1 ;; esac
30
+ [ -n "$hc_cwd" ] || return 0
31
+ case $hc_cwd in *'\'*|*[$'\n\r']*) return 1 ;; esac
32
+ hc_dir=${TMPDIR:-/tmp}
33
+ while [ "${hc_dir%/}" != "$hc_dir" ]; do hc_dir=${hc_dir%/}; done
34
+ shopt -q nullglob && hc_ng=1
35
+ shopt -s nullglob
36
+ hc_files=("$hc_dir"/halfcycle-live-"$hc_sid"/halfcycle-session-[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]-"$hc_sid".live"$hc_agent")
37
+ [ "$hc_ng" = 1 ] || shopt -u nullglob
38
+ [ "${#hc_files[@]}" -eq 1 ] || return 1
39
+ hc_file=$hc_dir/${hc_files[0]##*/}
40
+ IFS= read -r -d '' hc_text < "$hc_file" 2>/dev/null
41
+ [ "${hc_text%%$'\n'*}" = "$hc_cwd" ] || return 1
42
+ { printf '%s' "$hc_text" > "$hc_file"; } 2>/dev/null || return 1
43
+ hc_pt_live_text=$hc_text hc_pt_live_sid=$hc_sid hc_pt_live_agent=${hc_agent#-} hc_pt_live_dir=$hc_dir
44
+ return 0
45
+ }
46
+ halfcycle_pt_own_copy() {
47
+ local LC_ALL=C hc_val hc_top hc_rest hc_d hc_f hc_text= hc_ng=0 hc_others=0
48
+ local -a hc_files
49
+ halfcycle_pt_string tool_name || return 1
50
+ case $hc_val in
51
+ Write|Edit|MultiEdit) ;;
52
+ *) return 0 ;;
53
+ esac
54
+ halfcycle_pt_string file_path || return 1
55
+ case $hc_val in /*) ;; *) return 1 ;; esac
56
+ case $hc_val in *'\'*|*[$'\n\r']*) return 1 ;; esac
57
+ hc_rest=${hc_pt_live_text#*$'\n'}
58
+ hc_top=${hc_rest%%$'\n'*}
59
+ case $hc_top in /?*) ;; *) return 1 ;; esac
60
+ shopt -q nullglob && hc_ng=1
61
+ shopt -s nullglob
62
+ hc_files=("$hc_pt_live_dir"/halfcycle-live-"$hc_pt_live_sid"/halfcycle-session-[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]-"$hc_pt_live_sid".live-*)
63
+ [ "$hc_ng" = 1 ] || shopt -u nullglob
64
+ for hc_f in "${hc_files[@]}"; do
65
+ if [ -n "$hc_pt_live_agent" ]; then
66
+ case $hc_f in *.live-"$hc_pt_live_agent") continue ;; esac
67
+ fi
68
+ hc_others=1
69
+ done
70
+ case $hc_val in
71
+ "$hc_top"/?*) ;;
72
+ *) [ -z "$hc_pt_live_agent" ] && [ "$hc_others" = 0 ] && return 0; return 1 ;;
73
+ esac
74
+ hc_rest=${hc_val#"$hc_top"/}
75
+ case /$hc_rest/ in *//*|*/./*|*/../*) return 1 ;; esac
76
+ hc_d=$hc_top
77
+ while :; do
78
+ case $hc_rest in */*) ;; *) break ;; esac
79
+ hc_d=$hc_d/${hc_rest%%/*}
80
+ hc_rest=${hc_rest#*/}
81
+ [ -h "$hc_d" ] && return 1
82
+ [ -d "$hc_d" ] || break
83
+ { [ -e "$hc_d/.git" ] || [ -h "$hc_d/.git" ]; } && return 1
84
+ done
85
+ [ -z "$hc_pt_live_agent" ] && return 0
86
+ [ "$hc_others" = 0 ] && return 0
87
+ shopt -q nullglob && hc_ng=1
88
+ shopt -s nullglob
89
+ hc_files=("$hc_pt_live_dir"/halfcycle-live-"$hc_pt_live_sid"/halfcycle-session-[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]-"$hc_pt_live_sid".live)
90
+ [ "$hc_ng" = 1 ] || shopt -u nullglob
91
+ [ "${#hc_files[@]}" -eq 1 ] || return 1
92
+ IFS= read -r -d '' hc_text < "$hc_pt_live_dir/${hc_files[0]##*/}" 2>/dev/null
93
+ case $hc_text in *$'\n'*$'\n'*) ;; *) return 1 ;; esac
94
+ hc_text=${hc_text#*$'\n'}
95
+ hc_text=${hc_text%%$'\n'*}
96
+ [ -n "$hc_text" ] || return 1
97
+ [ "$hc_text" = "$hc_top" ] && return 1
98
+ return 0
99
+ }
100
+ halfcycle_pt_quiet() {
101
+ local LC_ALL=C hc_status hc_ext=0
102
+ shopt -q extglob && hc_ext=1
103
+ shopt -s nocasematch extglob
104
+ halfcycle_pt_quiet_input
105
+ hc_status=$?
106
+ shopt -u nocasematch
107
+ [ "$hc_ext" = 1 ] || shopt -u extglob
108
+ return $hc_status
109
+ }
110
+ halfcycle_pt_quiet_input() {
111
+ local hc_tool hc_val hc_c hc_w hc_q hc_a hc_marked
112
+ case $hc_input in '{'*'}'*) ;; *) return 1 ;; esac
113
+ case $hc_input in *$'\xe2\x84\xaa'*) return 1 ;; esac
114
+ halfcycle_pt_string tool_name || return 1
115
+ hc_tool=$hc_val
116
+ case $hc_input in *'"tool_input":{'*) ;; *) return 1 ;; esac
117
+ halfcycle_pt_once '"tool_input"' || return 1
118
+ case $hc_tool in
119
+ Bash)
120
+ halfcycle_pt_string command
121
+ case $? in 1) return 0 ;; 2) return 1 ;; esac
122
+ halfcycle_pt_normalise "$hc_val" || return 1
123
+ halfcycle_pt_glued && return 1
124
+ halfcycle_pt_words || return 1
125
+ halfcycle_pt_credential || return 1
126
+ halfcycle_pt_redirects
127
+ return ;;
128
+ Read)
129
+ halfcycle_pt_string file_path
130
+ case $? in 1) return 0 ;; 2) return 1 ;; esac
131
+ halfcycle_pt_normalise "$hc_val" || return 1
132
+ halfcycle_pt_credential
133
+ return ;;
134
+ Grep)
135
+ halfcycle_pt_string path
136
+ case $? in
137
+ 0) halfcycle_pt_normalise "$hc_val" || return 1; halfcycle_pt_credential || return 1 ;;
138
+ 2) return 1 ;;
139
+ esac
140
+ halfcycle_pt_string glob
141
+ case $? in 1) return 0 ;; 2) return 1 ;; esac
142
+ halfcycle_pt_normalise "$hc_val" || return 1
143
+ halfcycle_pt_credential
144
+ return ;;
145
+ Write|Edit|MultiEdit)
146
+ halfcycle_pt_string file_path
147
+ case $? in 1) return 0 ;; 2) return 1 ;; esac
148
+ case $hc_val in *'\'*) return 1 ;; esac
149
+ case $hc_val in /*|'~'*) ;; *) return 1 ;; esac
150
+ hc_c=$hc_val hc_marked=
151
+ halfcycle_pt_records
152
+ return ;;
153
+ *) return 1 ;;
154
+ esac
155
+ }
156
+ halfcycle_pt_once() {
157
+ local hc_rest
158
+ case $hc_input in *"$1"*) ;; *) return 1 ;; esac
159
+ hc_rest=${hc_input#*"$1"}
160
+ case $hc_rest in *"$1"*) return 1 ;; esac
161
+ return 0
162
+ }
163
+ halfcycle_pt_string() {
164
+ local hc_k="\"$1\"" hc_rest hc_piece hc_run hc_v=
165
+ hc_val=
166
+ case $hc_input in *"$hc_k"*) ;; *) return 1 ;; esac
167
+ hc_rest=${hc_input#*"$hc_k"}
168
+ case $hc_rest in *"$hc_k"*) return 2 ;; esac
169
+ case $hc_rest in ':"'*) hc_rest=${hc_rest#:\"} ;; *) return 2 ;; esac
170
+ while :; do
171
+ case $hc_rest in *'"'*) ;; *) return 2 ;; esac
172
+ hc_piece=${hc_rest%%\"*}
173
+ hc_rest=${hc_rest#*\"}
174
+ hc_run=${hc_piece##*[!\\]}
175
+ hc_v=$hc_v$hc_piece
176
+ case ${#hc_run} in *[13579]) hc_v=$hc_v\" ;; *) break ;; esac
177
+ done
178
+ hc_val=$hc_v
179
+ return 0
180
+ }
181
+ halfcycle_pt_optional() {
182
+ halfcycle_pt_string "$1"
183
+ case $? in
184
+ 0) if [ -n "$hc_val" ]; then hc_val="\"$hc_val\""; else hc_val=null; fi ;;
185
+ 1) hc_val=null ;;
186
+ *) return 1 ;;
187
+ esac
188
+ return 0
189
+ }
190
+ halfcycle_pt_normalise() {
191
+ local hc_v=$1 hc_m=$'\x01' hc_breaks=$' \x09\x0a\x0d\x0b\x0c;&|()<>`/$='
192
+ hc_w= hc_q= hc_c= hc_marked=
193
+ [ "${#hc_v}" -le 16384 ] || return 1
194
+ case $hc_v in *'\u'*|*"$hc_m"*) return 1 ;; esac
195
+ hc_v=${hc_v//\\\\/$hc_m}
196
+ hc_v=${hc_v//"$hc_m"\\n/}
197
+ hc_v=${hc_v//\\[nrtbf]/ }
198
+ hc_v=${hc_v//\\\//\/}
199
+ hc_v=${hc_v//\\\"/\"}
200
+ case $hc_v in *'\'*) return 1 ;; esac
201
+ case $hc_v in *"$hc_m"*) hc_marked=1 ;; esac
202
+ hc_q=$hc_v
203
+ hc_c=${hc_v//[\"\']/}
204
+ hc_w=" ${hc_c//[$hc_breaks]/ } "
205
+ hc_a=${hc_c//$hc_m/}
206
+ hc_a=${hc_a//[*?]/}
207
+ return 0
208
+ }
209
+ halfcycle_pt_glued() {
210
+ local hc_b=$' \x09\x0a\x0d\x0b\x0c;&|()<>`/=' hc_re
211
+ case $hc_c in *[A-Za-z0-9_]=*) ;; *) return 1 ;; esac
212
+ hc_re="[^$hc_b]\\$|}[^$hc_b\$]|\\$[A-Za-z_][A-Za-z0-9_]*[^A-Za-z0-9_$hc_b\$\\{]"
213
+ [[ $hc_c =~ $hc_re ]]
214
+ }
215
+ halfcycle_pt_has() {
216
+ if [ -n "$hc_marked" ]; then
217
+ [[ $hc_a == *"${1// /}"* && $hc_w == *$2* ]]
218
+ else
219
+ [[ $hc_w == *"$1"* ]]
220
+ fi
221
+ }
222
+ halfcycle_pt_words() {
223
+ halfcycle_pt_has ' rm ' $'[ \x01]?(\x01)r?(\x01)m[ \x01]' && return 1
224
+ halfcycle_pt_has ' unlink ' $'[ \x01]?(\x01)u?(\x01)n?(\x01)l?(\x01)i?(\x01)n?(\x01)k[ \x01]' && return 1
225
+ halfcycle_pt_has ' find ' $'[ \x01]?(\x01)f?(\x01)i?(\x01)n?(\x01)d[ \x01]' && { halfcycle_pt_has ' -delete ' $'[ \x01]?(\x01)-?(\x01)d?(\x01)e?(\x01)l?(\x01)e?(\x01)t?(\x01)e[ \x01]' || halfcycle_pt_has ' rm ' $'[ \x01]?(\x01)r?(\x01)m[ \x01]' || halfcycle_pt_has ' unlink ' $'[ \x01]?(\x01)u?(\x01)n?(\x01)l?(\x01)i?(\x01)n?(\x01)k[ \x01]'; } && return 1
226
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' rm ' $'[ \x01]?(\x01)r?(\x01)m[ \x01]' && return 1
227
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' reset ' $'[ \x01]?(\x01)r?(\x01)e?(\x01)s?(\x01)e?(\x01)t[ \x01]' && return 1
228
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' checkout ' $'[ \x01]?(\x01)c?(\x01)h?(\x01)e?(\x01)c?(\x01)k?(\x01)o?(\x01)u?(\x01)t[ \x01]' && return 1
229
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' switch ' $'[ \x01]?(\x01)s?(\x01)w?(\x01)i?(\x01)t?(\x01)c?(\x01)h[ \x01]' && return 1
230
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' restore ' $'[ \x01]?(\x01)r?(\x01)e?(\x01)s?(\x01)t?(\x01)o?(\x01)r?(\x01)e[ \x01]' && return 1
231
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' clean ' $'[ \x01]?(\x01)c?(\x01)l?(\x01)e?(\x01)a?(\x01)n[ \x01]' && return 1
232
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' stash ' $'[ \x01]?(\x01)s?(\x01)t?(\x01)a?(\x01)s?(\x01)h[ \x01]' && { halfcycle_pt_has ' drop ' $'[ \x01]?(\x01)d?(\x01)r?(\x01)o?(\x01)p[ \x01]' || halfcycle_pt_has ' clear ' $'[ \x01]?(\x01)c?(\x01)l?(\x01)e?(\x01)a?(\x01)r[ \x01]'; } && return 1
233
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' worktree ' $'[ \x01]?(\x01)w?(\x01)o?(\x01)r?(\x01)k?(\x01)t?(\x01)r?(\x01)e?(\x01)e[ \x01]' && halfcycle_pt_has ' remove ' $'[ \x01]?(\x01)r?(\x01)e?(\x01)m?(\x01)o?(\x01)v?(\x01)e[ \x01]' && return 1
234
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' push ' $'[ \x01]?(\x01)p?(\x01)u?(\x01)s?(\x01)h[ \x01]' && return 1
235
+ halfcycle_pt_has ' git ' $'[ \x01]?(\x01)g?(\x01)i?(\x01)t[ \x01]' && halfcycle_pt_has ' branch ' $'[ \x01]?(\x01)b?(\x01)r?(\x01)a?(\x01)n?(\x01)c?(\x01)h[ \x01]' && return 1
236
+ halfcycle_pt_has ' psql ' $'[ \x01]?(\x01)p?(\x01)s?(\x01)q?(\x01)l[ \x01]' && return 1
237
+ halfcycle_pt_has ' mysql ' $'[ \x01]?(\x01)m?(\x01)y?(\x01)s?(\x01)q?(\x01)l[ \x01]' && return 1
238
+ halfcycle_pt_has ' mariadb ' $'[ \x01]?(\x01)m?(\x01)a?(\x01)r?(\x01)i?(\x01)a?(\x01)d?(\x01)b[ \x01]' && return 1
239
+ halfcycle_pt_has ' sqlite3 ' $'[ \x01]?(\x01)s?(\x01)q?(\x01)l?(\x01)i?(\x01)t?(\x01)e?(\x01)3[ \x01]' && return 1
240
+ halfcycle_pt_has ' dropdb ' $'[ \x01]?(\x01)d?(\x01)r?(\x01)o?(\x01)p?(\x01)d?(\x01)b[ \x01]' && return 1
241
+ halfcycle_pt_has ' redis-cli ' $'[ \x01]?(\x01)r?(\x01)e?(\x01)d?(\x01)i?(\x01)s?(\x01)-?(\x01)c?(\x01)l?(\x01)i[ \x01]' && return 1
242
+ halfcycle_pt_has ' prisma ' $'[ \x01]?(\x01)p?(\x01)r?(\x01)i?(\x01)s?(\x01)m?(\x01)a[ \x01]' && return 1
243
+ halfcycle_pt_has ' rails ' $'[ \x01]?(\x01)r?(\x01)a?(\x01)i?(\x01)l?(\x01)s[ \x01]' && return 1
244
+ halfcycle_pt_has ' rake ' $'[ \x01]?(\x01)r?(\x01)a?(\x01)k?(\x01)e[ \x01]' && return 1
245
+ halfcycle_pt_has ' bundle ' $'[ \x01]?(\x01)b?(\x01)u?(\x01)n?(\x01)d?(\x01)l?(\x01)e[ \x01]' && return 1
246
+ halfcycle_pt_has ' auth ' $'[ \x01]?(\x01)a?(\x01)u?(\x01)t?(\x01)h[ \x01]' && return 1
247
+ halfcycle_pt_has ' find-generic-password ' $'[ \x01]?(\x01)f?(\x01)i?(\x01)n?(\x01)d?(\x01)-?(\x01)g?(\x01)e?(\x01)n?(\x01)e?(\x01)r?(\x01)i?(\x01)c?(\x01)-?(\x01)p?(\x01)a?(\x01)s?(\x01)s?(\x01)w?(\x01)o?(\x01)r?(\x01)d[ \x01]' && return 1
248
+ halfcycle_pt_has ' find-internet-password ' $'[ \x01]?(\x01)f?(\x01)i?(\x01)n?(\x01)d?(\x01)-?(\x01)i?(\x01)n?(\x01)t?(\x01)e?(\x01)r?(\x01)n?(\x01)e?(\x01)t?(\x01)-?(\x01)p?(\x01)a?(\x01)s?(\x01)s?(\x01)w?(\x01)o?(\x01)r?(\x01)d[ \x01]' && return 1
249
+ halfcycle_pt_has ' publish ' $'[ \x01]?(\x01)p?(\x01)u?(\x01)b?(\x01)l?(\x01)i?(\x01)s?(\x01)h[ \x01]' && return 1
250
+ halfcycle_pt_has ' npm ' $'[ \x01]?(\x01)n?(\x01)p?(\x01)m[ \x01]' && halfcycle_pt_has ' publish ' $'[ \x01]?(\x01)p?(\x01)u?(\x01)b?(\x01)l?(\x01)i?(\x01)s?(\x01)h[ \x01]' && return 1
251
+ halfcycle_pt_has ' upload ' $'[ \x01]?(\x01)u?(\x01)p?(\x01)l?(\x01)o?(\x01)a?(\x01)d[ \x01]' && return 1
252
+ halfcycle_pt_has ' push ' $'[ \x01]?(\x01)p?(\x01)u?(\x01)s?(\x01)h[ \x01]' && return 1
253
+ halfcycle_pt_has ' release ' $'[ \x01]?(\x01)r?(\x01)e?(\x01)l?(\x01)e?(\x01)a?(\x01)s?(\x01)e[ \x01]' && halfcycle_pt_has ' create ' $'[ \x01]?(\x01)c?(\x01)r?(\x01)e?(\x01)a?(\x01)t?(\x01)e[ \x01]' && return 1
254
+ halfcycle_pt_has ' nuget ' $'[ \x01]?(\x01)n?(\x01)u?(\x01)g?(\x01)e?(\x01)t[ \x01]' && halfcycle_pt_has ' push ' $'[ \x01]?(\x01)p?(\x01)u?(\x01)s?(\x01)h[ \x01]' && return 1
255
+ halfcycle_pt_has ' deploy ' $'[ \x01]?(\x01)d?(\x01)e?(\x01)p?(\x01)l?(\x01)o?(\x01)y[ \x01]' && return 1
256
+ halfcycle_pt_has ' apply ' $'[ \x01]?(\x01)a?(\x01)p?(\x01)p?(\x01)l?(\x01)y[ \x01]' && return 1
257
+ halfcycle_pt_has ' destroy ' $'[ \x01]?(\x01)d?(\x01)e?(\x01)s?(\x01)t?(\x01)r?(\x01)o?(\x01)y[ \x01]' && return 1
258
+ halfcycle_pt_has ' delete ' $'[ \x01]?(\x01)d?(\x01)e?(\x01)l?(\x01)e?(\x01)t?(\x01)e[ \x01]' && return 1
259
+ halfcycle_pt_has ' --prod ' $'[ \x01]?(\x01)-?(\x01)-?(\x01)p?(\x01)r?(\x01)o?(\x01)d[ \x01]' && return 1
260
+ halfcycle_pt_has ' --production ' $'[ \x01]?(\x01)-?(\x01)-?(\x01)p?(\x01)r?(\x01)o?(\x01)d?(\x01)u?(\x01)c?(\x01)t?(\x01)i?(\x01)o?(\x01)n[ \x01]' && return 1
261
+ halfcycle_pt_has ' truncate ' $'[ \x01]?(\x01)t?(\x01)r?(\x01)u?(\x01)n?(\x01)c?(\x01)a?(\x01)t?(\x01)e[ \x01]' && return 1
262
+ halfcycle_pt_has ' cp ' $'[ \x01]?(\x01)c?(\x01)p[ \x01]' && halfcycle_pt_has ' dev ' $'[ \x01]?(\x01)d?(\x01)e?(\x01)v[ \x01]' && halfcycle_pt_has ' null ' $'[ \x01]?(\x01)n?(\x01)u?(\x01)l?(\x01)l[ \x01]' && return 1
263
+ return 0
264
+ }
265
+ halfcycle_pt_in() {
266
+ if [ -n "$hc_marked" ]; then
267
+ [[ $hc_a == *"${1// /}"* && ( $hc_x == *$2* || $hc_y == *$2* ) ]] ||
268
+ [[ ${hc_y//$'\x01'/} == *"${1// /}"* && $hc_y == *$2* ]]
269
+ else
270
+ [[ $hc_x == *"$1"* || $hc_y == *"$1"* ]]
271
+ fi
272
+ }
273
+ halfcycle_pt_credential() {
274
+ local hc_x=${hc_w//[*?]/} hc_y=${hc_w//[*?]/x}
275
+ halfcycle_pt_in ' .env' $'[ \x01]?(\x01).?(\x01)e?(\x01)n?(\x01)v' && return 1
276
+ halfcycle_pt_in ' id_rsa' $'[ \x01]?(\x01)i?(\x01)d?(\x01)_?(\x01)r?(\x01)s?(\x01)a' && return 1
277
+ halfcycle_pt_in ' id_ed25519' $'[ \x01]?(\x01)i?(\x01)d?(\x01)_?(\x01)e?(\x01)d?(\x01)2?(\x01)5?(\x01)5?(\x01)1?(\x01)9' && return 1
278
+ halfcycle_pt_in ' id_ecdsa' $'[ \x01]?(\x01)i?(\x01)d?(\x01)_?(\x01)e?(\x01)c?(\x01)d?(\x01)s?(\x01)a' && return 1
279
+ halfcycle_pt_in ' service-account' $'[ \x01]?(\x01)s?(\x01)e?(\x01)r?(\x01)v?(\x01)i?(\x01)c?(\x01)e?(\x01)-?(\x01)a?(\x01)c?(\x01)c?(\x01)o?(\x01)u?(\x01)n?(\x01)t' && return 1
280
+ halfcycle_pt_in '.pem ' $'.?(\x01)p?(\x01)e?(\x01)m[ \x01]' && return 1
281
+ halfcycle_pt_in '.key ' $'.?(\x01)k?(\x01)e?(\x01)y[ \x01]' && return 1
282
+ halfcycle_pt_in '.p12 ' $'.?(\x01)p?(\x01)1?(\x01)2[ \x01]' && return 1
283
+ halfcycle_pt_in '.pfx ' $'.?(\x01)p?(\x01)f?(\x01)x[ \x01]' && return 1
284
+ halfcycle_pt_in ' .netrc ' $'[ \x01]?(\x01).?(\x01)n?(\x01)e?(\x01)t?(\x01)r?(\x01)c[ \x01]' && return 1
285
+ halfcycle_pt_in ' .npmrc ' $'[ \x01]?(\x01).?(\x01)n?(\x01)p?(\x01)m?(\x01)r?(\x01)c[ \x01]' && return 1
286
+ halfcycle_pt_in ' .pypirc ' $'[ \x01]?(\x01).?(\x01)p?(\x01)y?(\x01)p?(\x01)i?(\x01)r?(\x01)c[ \x01]' && return 1
287
+ halfcycle_pt_in ' credentials.json ' $'[ \x01]?(\x01)c?(\x01)r?(\x01)e?(\x01)d?(\x01)e?(\x01)n?(\x01)t?(\x01)i?(\x01)a?(\x01)l?(\x01)s?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n[ \x01]' && return 1
288
+ halfcycle_pt_in ' .credentials.json ' $'[ \x01]?(\x01).?(\x01)c?(\x01)r?(\x01)e?(\x01)d?(\x01)e?(\x01)n?(\x01)t?(\x01)i?(\x01)a?(\x01)l?(\x01)s?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n[ \x01]' && return 1
289
+ halfcycle_pt_in ' .aws ' $'[ \x01]?(\x01).?(\x01)a?(\x01)w?(\x01)s[ \x01]' && halfcycle_pt_in ' credentials ' $'[ \x01]?(\x01)c?(\x01)r?(\x01)e?(\x01)d?(\x01)e?(\x01)n?(\x01)t?(\x01)i?(\x01)a?(\x01)l?(\x01)s[ \x01]' && return 1
290
+ halfcycle_pt_in ' .docker ' $'[ \x01]?(\x01).?(\x01)d?(\x01)o?(\x01)c?(\x01)k?(\x01)e?(\x01)r[ \x01]' && halfcycle_pt_in ' config.json ' $'[ \x01]?(\x01)c?(\x01)o?(\x01)n?(\x01)f?(\x01)i?(\x01)g?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n[ \x01]' && return 1
291
+ halfcycle_pt_in ' .config ' $'[ \x01]?(\x01).?(\x01)c?(\x01)o?(\x01)n?(\x01)f?(\x01)i?(\x01)g[ \x01]' && halfcycle_pt_in ' gh ' $'[ \x01]?(\x01)g?(\x01)h[ \x01]' && halfcycle_pt_in ' hosts.yml ' $'[ \x01]?(\x01)h?(\x01)o?(\x01)s?(\x01)t?(\x01)s?(\x01).?(\x01)y?(\x01)m?(\x01)l[ \x01]' && return 1
292
+ halfcycle_pt_in ' .halfcycle ' $'[ \x01]?(\x01).?(\x01)h?(\x01)a?(\x01)l?(\x01)f?(\x01)c?(\x01)y?(\x01)c?(\x01)l?(\x01)e[ \x01]' && halfcycle_pt_in ' account.json ' $'[ \x01]?(\x01)a?(\x01)c?(\x01)c?(\x01)o?(\x01)u?(\x01)n?(\x01)t?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n[ \x01]' && return 1
293
+ halfcycle_pt_in ' .halfcycle ' $'[ \x01]?(\x01).?(\x01)h?(\x01)a?(\x01)l?(\x01)f?(\x01)c?(\x01)y?(\x01)c?(\x01)l?(\x01)e[ \x01]' && halfcycle_pt_in ' engagements ' $'[ \x01]?(\x01)e?(\x01)n?(\x01)g?(\x01)a?(\x01)g?(\x01)e?(\x01)m?(\x01)e?(\x01)n?(\x01)t?(\x01)s[ \x01]' && halfcycle_pt_in ' env ' $'[ \x01]?(\x01)e?(\x01)n?(\x01)v[ \x01]' && return 1
294
+ return 0
295
+ }
296
+ halfcycle_pt_holds() {
297
+ if [ -n "$hc_marked" ]; then
298
+ [[ $hc_a == *"$1"* && $hc_c == *$2* ]]
299
+ else
300
+ [[ $hc_c == *"$1"* ]]
301
+ fi
302
+ }
303
+ halfcycle_pt_records() {
304
+ halfcycle_pt_holds 'ENGAGEMENT.md' $'E?(\x01)N?(\x01)G?(\x01)A?(\x01)G?(\x01)E?(\x01)M?(\x01)E?(\x01)N?(\x01)T?(\x01).?(\x01)m?(\x01)d' && return 1
305
+ halfcycle_pt_holds 'project-log.md' $'p?(\x01)r?(\x01)o?(\x01)j?(\x01)e?(\x01)c?(\x01)t?(\x01)-?(\x01)l?(\x01)o?(\x01)g?(\x01).?(\x01)m?(\x01)d' && return 1
306
+ halfcycle_pt_holds 'product-spec.md' $'p?(\x01)r?(\x01)o?(\x01)d?(\x01)u?(\x01)c?(\x01)t?(\x01)-?(\x01)s?(\x01)p?(\x01)e?(\x01)c?(\x01).?(\x01)m?(\x01)d' && return 1
307
+ halfcycle_pt_holds 'architecture-overview.md' $'a?(\x01)r?(\x01)c?(\x01)h?(\x01)i?(\x01)t?(\x01)e?(\x01)c?(\x01)t?(\x01)u?(\x01)r?(\x01)e?(\x01)-?(\x01)o?(\x01)v?(\x01)e?(\x01)r?(\x01)v?(\x01)i?(\x01)e?(\x01)w?(\x01).?(\x01)m?(\x01)d' && return 1
308
+ halfcycle_pt_holds 'invariants.md' $'i?(\x01)n?(\x01)v?(\x01)a?(\x01)r?(\x01)i?(\x01)a?(\x01)n?(\x01)t?(\x01)s?(\x01).?(\x01)m?(\x01)d' && return 1
309
+ halfcycle_pt_holds 'system.json' $'s?(\x01)y?(\x01)s?(\x01)t?(\x01)e?(\x01)m?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n' && return 1
310
+ halfcycle_pt_holds 'phases' $'p?(\x01)h?(\x01)a?(\x01)s?(\x01)e?(\x01)s' && return 1
311
+ halfcycle_pt_holds 'features' $'f?(\x01)e?(\x01)a?(\x01)t?(\x01)u?(\x01)r?(\x01)e?(\x01)s' && return 1
312
+ halfcycle_pt_holds 'decisions' $'d?(\x01)e?(\x01)c?(\x01)i?(\x01)s?(\x01)i?(\x01)o?(\x01)n?(\x01)s' && return 1
313
+ halfcycle_pt_holds 'build-tasks.json' $'b?(\x01)u?(\x01)i?(\x01)l?(\x01)d?(\x01)-?(\x01)t?(\x01)a?(\x01)s?(\x01)k?(\x01)s?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n' && return 1
314
+ halfcycle_pt_holds 'task-plan.json' $'t?(\x01)a?(\x01)s?(\x01)k?(\x01)-?(\x01)p?(\x01)l?(\x01)a?(\x01)n?(\x01).?(\x01)j?(\x01)s?(\x01)o?(\x01)n' && return 1
315
+ halfcycle_pt_holds 'build-records' $'b?(\x01)u?(\x01)i?(\x01)l?(\x01)d?(\x01)-?(\x01)r?(\x01)e?(\x01)c?(\x01)o?(\x01)r?(\x01)d?(\x01)s' && return 1
316
+ halfcycle_pt_holds '.claude' $'.?(\x01)c?(\x01)l?(\x01)a?(\x01)u?(\x01)d?(\x01)e' && return 1
317
+ return 0
318
+ }
319
+ halfcycle_pt_redirects() {
320
+ local hc_s=$hc_c hc_rest hc_pre hc_qc hc_body hc_abs= hc_bt=$'\x60'
321
+ case $hc_c in *'>'*) ;; *) return 0 ;; esac
322
+ if [ -z "$hc_marked" ] && ! { halfcycle_pt_has ' bash ' $'[ \x01]?(\x01)b?(\x01)a?(\x01)s?(\x01)h[ \x01]' || halfcycle_pt_has ' sh ' $'[ \x01]?(\x01)s?(\x01)h[ \x01]' || halfcycle_pt_has ' zsh ' $'[ \x01]?(\x01)z?(\x01)s?(\x01)h[ \x01]' || halfcycle_pt_has ' dash ' $'[ \x01]?(\x01)d?(\x01)a?(\x01)s?(\x01)h[ \x01]' || halfcycle_pt_has ' ksh ' $'[ \x01]?(\x01)k?(\x01)s?(\x01)h[ \x01]' || halfcycle_pt_has ' eval ' $'[ \x01]?(\x01)e?(\x01)v?(\x01)a?(\x01)l[ \x01]' || halfcycle_pt_has ' -S ' $'[ \x01]?(\x01)-?(\x01)S[ \x01]' || halfcycle_pt_has ' --split-string ' $'[ \x01]?(\x01)-?(\x01)-?(\x01)s?(\x01)p?(\x01)l?(\x01)i?(\x01)t?(\x01)-?(\x01)s?(\x01)t?(\x01)r?(\x01)i?(\x01)n?(\x01)g[ \x01]'; }; then
323
+ case $hc_q in *'<<'*|*'${'*|*'$(('*) ;; *)
324
+ case " $hc_q" in *[[:space:]\;\&\|\(\)$hc_bt]'#'*) ;; *)
325
+ hc_s= hc_rest=$hc_q
326
+ while :; do
327
+ case $hc_rest in *[\"\']*) ;; *) hc_s=$hc_s$hc_rest; break ;; esac
328
+ hc_pre=${hc_rest%%[\"\']*}
329
+ hc_rest=${hc_rest:${#hc_pre}}
330
+ hc_qc=${hc_rest:0:1}
331
+ hc_rest=${hc_rest:1}
332
+ case $hc_rest in *"$hc_qc"*) ;; *) return 1 ;; esac
333
+ hc_body=${hc_rest%%"$hc_qc"*}
334
+ hc_rest=${hc_rest#*"$hc_qc"}
335
+ case $hc_qc$hc_body in '"'*'$('*|'"'*"$hc_bt"*) hc_s=$hc_c; break ;; esac
336
+ hc_s=$hc_s$hc_pre${hc_body//'>'/}
337
+ done
338
+ ;;
339
+ esac
340
+ ;;
341
+ esac
342
+ fi
343
+ while :; do
344
+ case $hc_s in *'>'*) ;; *) break ;; esac
345
+ hc_s=${hc_s#*'>'}
346
+ case $hc_s in
347
+ '>'*) hc_s=${hc_s#'>'}; continue ;;
348
+ '&'*|'('*) continue ;;
349
+ '|'*) hc_s=${hc_s#'|'} ;;
350
+ esac
351
+ hc_pre=${hc_s%%[![:space:]]*}
352
+ hc_pre=${hc_s:${#hc_pre}}
353
+ case $hc_pre in /*|'~'*) hc_abs=1 ;; *) return 1 ;; esac
354
+ done
355
+ [ -z "$hc_abs" ] && return 0
356
+ halfcycle_pt_records
357
+ }
358
+ halfcycle_pt_resolve() {
359
+ local - IFS=/ hc_part hc_out=
360
+ local -a hc_parts
361
+ set -f
362
+ hc_parts=($1)
363
+ for hc_part in "${hc_parts[@]}"; do
364
+ case $hc_part in
365
+ ''|.) ;;
366
+ ..) hc_out=${hc_out%/*} ;;
367
+ *) hc_out=$hc_out/$hc_part ;;
368
+ esac
369
+ done
370
+ hc_path=${hc_out:-/}
371
+ }
372
+ halfcycle_pt_branch() {
373
+ local hc_dir hc_dotgit hc_gitdir hc_text hc_line hc_ptr hc_path
374
+ hc_branch=null
375
+ case $1 in /*) ;; *) return 0 ;; esac
376
+ hc_dir=$1
377
+ while [ -n "$hc_dir" ] && [ "$hc_dir" != / ]; do
378
+ [ -L "$hc_dir" ] && return 1
379
+ hc_dir=${hc_dir%/*}
380
+ done
381
+ halfcycle_pt_resolve "$1"
382
+ hc_dir=$hc_path
383
+ [ -d "$hc_dir" ] || return 0
384
+ while :; do
385
+ hc_dotgit=${hc_dir%/}/.git
386
+ if [ -e "$hc_dotgit" ]; then
387
+ if [ -d "$hc_dotgit" ]; then
388
+ hc_gitdir=$hc_dotgit
389
+ else
390
+ hc_text=
391
+ IFS= read -r -d '' hc_text < "$hc_dotgit" 2>/dev/null
392
+ hc_ptr=
393
+ while IFS= read -r hc_line || [ -n "$hc_line" ]; do
394
+ case $hc_line in
395
+ gitdir:*)
396
+ hc_line=${hc_line#gitdir:}
397
+ hc_line=${hc_line#"${hc_line%%[!$' \t']*}"}
398
+ hc_line=${hc_line%"${hc_line##*[!$' \t']}"}
399
+ [ -n "$hc_line" ] && { hc_ptr=$hc_line; break; }
400
+ ;;
401
+ esac
402
+ done <<< "$hc_text"
403
+ [ -n "$hc_ptr" ] || return 0
404
+ case $hc_ptr in /*) halfcycle_pt_resolve "$hc_ptr" ;; *) halfcycle_pt_resolve "$hc_dir/$hc_ptr" ;; esac
405
+ hc_gitdir=$hc_path
406
+ fi
407
+ hc_text=
408
+ IFS= read -r -d '' hc_text < "$hc_gitdir/HEAD" 2>/dev/null || [ -n "$hc_text" ] || return 0
409
+ case $hc_text in *[!$'\x01'-$'\x7f']*) return 1 ;; esac
410
+ hc_text=${hc_text#"${hc_text%%[![:space:]]*}"}
411
+ hc_text=${hc_text%"${hc_text##*[![:space:]]}"}
412
+ case $hc_text in ref:*) ;; *) return 0 ;; esac
413
+ hc_text=${hc_text#ref:}
414
+ hc_text=${hc_text#"${hc_text%%[!$' \t']*}"}
415
+ case $hc_text in refs/heads/?*) ;; *) return 0 ;; esac
416
+ hc_text=${hc_text#refs/heads/}
417
+ case $hc_text in *[$'\n\r']*) return 0 ;; esac
418
+ case $hc_text in *[$'\x01'-$'\x1f']*) return 1 ;; esac
419
+ hc_text=${hc_text//\\/\\\\}
420
+ hc_text=${hc_text//\"/\\\"}
421
+ hc_branch="\"$hc_text\""
422
+ return 0
423
+ fi
424
+ [ "$hc_dir" = / ] && return 0
425
+ hc_dir=${hc_dir%/*}
426
+ [ -n "$hc_dir" ] || hc_dir=/
427
+ done
428
+ }
429
+ halfcycle_pt_time_line() {
430
+ local LC_ALL=C hc_val hc_id hc_home hc_folder hc_branch hc_at hc_secs hc_frac hc_dir hc_line hc_f_session_id hc_f_agent_id hc_f_transcript_path hc_f_tool_use_id
431
+ local hc_re='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
432
+ [ -n "${GUARD_SERVICE_TOKEN:-}" ] || return 0
433
+ [ -n "${GUARD_ENGAGEMENT_ID:-}" ] || return 0
434
+ hc_id=$GUARD_ENGAGEMENT_ID
435
+ [[ $hc_id =~ $hc_re ]] || return 0
436
+ hc_home=${HOME:-}
437
+ [ -n "$hc_home" ] || return 1
438
+ halfcycle_pt_optional 'session_id' || return 1
439
+ hc_f_session_id=$hc_val
440
+ halfcycle_pt_optional 'agent_id' || return 1
441
+ hc_f_agent_id=$hc_val
442
+ halfcycle_pt_optional 'transcript_path' || return 1
443
+ hc_f_transcript_path=$hc_val
444
+ halfcycle_pt_optional 'tool_use_id' || return 1
445
+ hc_f_tool_use_id=$hc_val
446
+ halfcycle_pt_string cwd
447
+ case $? in
448
+ 0) hc_folder=$hc_val ;;
449
+ 1) hc_folder= ;;
450
+ *) return 1 ;;
451
+ esac
452
+ case $hc_folder in *'\'*) return 1 ;; esac
453
+ if [ -z "$hc_folder" ]; then
454
+ cd -P . 2>/dev/null || return 1
455
+ hc_folder=$PWD
456
+ case $hc_folder in *[$'\x01'-$'\x1f']*|*'\'*|*'"'*) return 1 ;; esac
457
+ fi
458
+ halfcycle_pt_branch "$hc_folder" || return 1
459
+ hc_secs=${1%%[.,]*}
460
+ hc_frac=${1#*[.,]}
461
+ TZ=UTC0 printf -v hc_at '%(%Y-%m-%dT%H:%M:%S)T' "$hc_secs" || return 1
462
+ hc_at=$hc_at.${hc_frac:0:3}Z
463
+ printf -v hc_line '{"at":"%s","event":"action","session":%s,"agent":%s,"folder":"%s","branch":%s,"transcript":%s,"toolUse":%s}' "$hc_at" "$hc_f_session_id" "$hc_f_agent_id" "$hc_folder" "$hc_branch" "$hc_f_transcript_path" "$hc_f_tool_use_id"
464
+ hc_dir=$hc_home/.halfcycle/engagements/$hc_id/time-log
465
+ umask 077
466
+ { printf '%s\n' "$hc_line" >> "$hc_dir/events.jsonl"; } 2>/dev/null ||
467
+ { mkdir -p "$hc_dir" && printf '%s\n' "$hc_line" >> "$hc_dir/events.jsonl"; } 2>/dev/null
468
+ return 0
469
+ }
470
+ # --- end Halfcycle cheap pass ---------------------------------------------
package/dist/bin.d.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  * halfcycle draw architecture|phase <phase> — draw the architecture or a phase's pictures into docs/diagrams (`draw/`)
16
16
  * halfcycle plan <phase> [--show] — check a phase's plan and report it; list it (`plan.ts`)
17
17
  * halfcycle review <phase> --show | --answer <part> "…" — list a phase's close review; answer a question (`review.ts`)
18
+ * halfcycle prove <phase> <task> --label "…" (--before|--after) [-- <cmd>] — run a check once on this machine and record what it saw (`prove.ts`)
18
19
  * halfcycle uninstall [--remove-credential] — take back what the installer wrote here
19
20
  *
20
21
  * NO `ci` VERB (T-25, operator decision 2026-09-22(c)) — `ci bind`/`ci unbind`
package/dist/bin.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG"}
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The file discipline every record-pair writer shares: exclusive create, the
3
+ * write-then-rename replace, and the no-half-pair create of a `.json` beside its `.md`.
4
+ *
5
+ * ONE COPY FOR TWO WRITERS. A phase's Build Record (`write.ts`, `writeRecordPair`) and
6
+ * a small change's record (`write-change.ts`, `writeChangeRecordPair`) write their pairs
7
+ * through these helpers, and `draw`'s three-file writer goes through the same seam, so
8
+ * there is one exclusive-create on disk, not one per writer. What each writer decides
9
+ * for itself is only what to do when a file is already in the way: the phase writer may
10
+ * replace its own earlier record, the small-change writer, whose record is write-once,
11
+ * never replaces.
12
+ *
13
+ * Nothing here builds a path. Every caller composes and checks its own paths before it
14
+ * hands them over (INV-023).
15
+ */
16
+ /**
17
+ * The file operations a pair writer makes, behind one seam so a test can make
18
+ * the SECOND write fail and watch the first come back out. `createExclusive` opens
19
+ * with `wx` (never truncates an existing file) and removes what it created if the
20
+ * write itself fails; `rename` replaces in one step on the same filesystem.
21
+ *
22
+ * ONE SEAM FOR TEXT AND BYTES. `draw`'s three-file writer (`../draw/write.ts`) goes
23
+ * through this same seam with a PNG's bytes, so a string is written as UTF-8 and a
24
+ * byte array as itself.
25
+ */
26
+ export interface RecordPairIo {
27
+ createExclusive(path: string, data: string | Uint8Array): void;
28
+ rename(from: string, to: string): void;
29
+ }
30
+ export declare const NODE_RECORD_PAIR_IO: RecordPairIo;
31
+ /** Serialise a served record for the `.json`: 2-space indent, trailing newline. */
32
+ export declare function serialiseServedRecord(rawRecord: unknown): string;
33
+ /** Is `err` the file system saying the file is already there? */
34
+ export declare function isEexist(err: unknown): boolean;
35
+ /** The text at `path`, or `null` when it cannot be read as a file. */
36
+ export declare function readIfPresent(path: string): string | null;
37
+ /**
38
+ * Create both files of a pair exclusively, `.json` first. `created`: both are down.
39
+ * `exists`: one of the two was already there, and nothing this call created is left
40
+ * behind (a `.json` it created is removed again), so the caller's own replace rule
41
+ * decides from a clean state. Any other failure is thrown, with nothing left behind.
42
+ */
43
+ export declare function createPairExclusive(io: RecordPairIo, jsonPath: string, jsonText: string, mdPath: string, mdText: string): 'created' | 'exists';
44
+ /**
45
+ * Write `text` to `tmp` and rename it over `finalPath`. A `.tmp` left by an earlier
46
+ * crash is removed first and never read. On failure the temporary file is removed.
47
+ */
48
+ export declare function replaceOne(io: RecordPairIo, tmp: string, finalPath: string, text: string): void;
49
+ //# sourceMappingURL=pair-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair-io.d.ts","sourceRoot":"","sources":["../../src/build-record/pair-io.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAC/D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AA2BD,eAAO,MAAM,mBAAmB,EAAE,YAGjC,CAAC;AAEF,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAEhE;AAED,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE9C;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,SAAS,GAAG,QAAQ,CAYtB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAS/F"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The Markdown half of a small change's record pair (feature `three-paths`, *The
3
+ * writer*): `renderChangeRecordMarkdown` turns a frozen `smallChangeRecordSchema` value
4
+ * into the `.md` written beside its `.json` under `docs/build-records/changes/`.
5
+ *
6
+ * A SMALL CHANGE IS NOT A PHASE, and its record never reads as one: no line here says
7
+ * approved, unapproved, or asks for an approval (three-paths AC6, INV-024). A small
8
+ * change carries no approval to give or to lack, so the words never appear.
9
+ *
10
+ * WHOSE WORDS ARE WHOSE (INV-025). What Halfcycle counted is under "What Halfcycle
11
+ * saw"; the agent's one line, a reviewer's finding, a diagnosis and a call are each
12
+ * marked as the agent's own. A change that ended without a count (stopped, or moved up
13
+ * by the person) says Halfcycle did not count it, never that nothing changed. A window
14
+ * with no recorded check run reads as not observed, never as nothing found (INV-021).
15
+ *
16
+ * Every string from the record is printed through the Build Record renderer's own
17
+ * escapers (`printRecordText` for a slot with a line of its own, `printStrictText` for
18
+ * a slot sharing a line), so record text cannot become markup. One home for that rule.
19
+ *
20
+ * SHIPS TO CLIENT DISK inside the CLI: no comment sits beside an object member.
21
+ */
22
+ import type { SmallChangeRecord } from '@halfcycle/events';
23
+ /** The `.md` of a small change's record. Pure: the same record renders the same text. */
24
+ export declare function renderChangeRecordMarkdown(record: SmallChangeRecord): string;
25
+ //# sourceMappingURL=render-change.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-change.d.ts","sourceRoot":"","sources":["../../src/build-record/render-change.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAsH3D,yFAAyF;AACzF,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CA4C5E"}
@@ -16,10 +16,12 @@
16
16
  * bytes, whatever key order a database hands back.
17
17
  *
18
18
  * IT DOES NOT PARSE. The caller parses the record first; the type here is the
19
- * contract's own inferred type. Two of the contract's functions are imported at
19
+ * contract's own inferred type. Four of the contract's functions are imported at
20
20
  * runtime, because each is the one home of what it composes: `codeLinkLine`, the
21
- * code line every reader prints for a part, and `removalsOf`, the parts a closed
22
- * review leaves to take out of the system description. Neither is restated here.
21
+ * code line every reader prints for a part; `removalsOf`, the parts a closed
22
+ * review leaves to take out of the system description; `hasOwnerReview`, whether a
23
+ * record kept the owner's review (v3 and v4 did, v2 did not); and `keptPathOf`, the
24
+ * phase's path a v4 record kept. None is restated here, and no format is compared.
23
25
  * The one cross-half rule it relies
24
26
  * on — both halves describe the same closes, paired by `sequence` — it checks
25
27
  * itself and throws on, because printing a close without its other half would
@@ -77,8 +79,11 @@ export declare function latestCloseHeadline(record: BuildRecord): string;
77
79
  * learned an earlier round said something else).
78
80
  *
79
81
  * Render a parsed Build Record as markdown: v2's observed and attested halves, which
80
- * a v3 record carries unchanged, and for a v3 record the owner's review after the
81
- * latest close (RM1–RM3, feature `close-review-and-carry-drop`).
82
+ * a v3 or v4 record carries unchanged; for a v4 record its path; for a v3 or v4
83
+ * record the owner's review after the latest close (RM1–RM3, feature
84
+ * `close-review-and-carry-drop`); and for a v4 record alone the calls made without asking
85
+ * (C1–C5) and the owner's answers to checks Halfcycle never ran, after the review. An
86
+ * older record prints neither section, so its Markdown is unchanged.
82
87
  *
83
88
  * Throws, in client words, if the two halves describe different closes — which a
84
89
  * parsed record cannot do. Output uses `\n` only and ends in exactly one `\n`.
@@ -1 +1 @@
1
- {"version":3,"file":"render-v2.d.ts","sourceRoot":"","sources":["../../src/build-record/render-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAGL,KAAK,WAAW,EAKjB,MAAM,mBAAmB,CAAC;AA8J3B;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAepD;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASpD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAgMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAO/D;AAmRD;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAyEvE"}
1
+ {"version":3,"file":"render-v2.d.ts","sourceRoot":"","sources":["../../src/build-record/render-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAgBL,KAAK,WAAW,EAQjB,MAAM,mBAAmB,CAAC;AA8J3B;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAepD;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASpD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AA2MD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAO/D;AAyTD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAgFvE"}