cc-discipline 2.15.1 → 2.15.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-discipline",
3
- "version": "2.15.1",
3
+ "version": "2.15.2",
4
4
  "description": "Discipline framework for Claude Code — rules, hooks, and agents that keep AI on track",
5
5
  "bin": {
6
6
  "cc-discipline": "bin/cli.js"
@@ -643,7 +643,12 @@ AWK
643
643
  if [ ${#INPUT} -gt 65536 ]; then
644
644
  VERDICT=TOOLONG
645
645
  else
646
- VERDICT=$(awk "$PROG" <<<"$INPUT" 2>/dev/null)
646
+ # awk runs with LC_ALL=C: byte semantics everywhere, which is how this code is
647
+ # written. Under a UTF-8 locale, macOS's awk is still byte-based for substr()
648
+ # and length(), but a regex match against half a multi-byte character is
649
+ # fatal ("towc: multibyte conversion failure"). 2.15.1 died that way on
650
+ # "## 2026-08-04(三)" and fell back to the file's tail (2026-09-25).
651
+ VERDICT=$(LC_ALL=C awk "$PROG" <<<"$INPUT" 2>/dev/null)
647
652
  fi
648
653
  TAB=$'\t'
649
654
  NOTE="" # never inherited from the environment
@@ -43,6 +43,11 @@ fi
43
43
  # condition" is only said when every item actually has one.
44
44
  # Comment state is tracked on every line, before any heading test, so a heading
45
45
  # written inside <!-- ... --> neither opens nor closes a section.
46
+ # awk runs with LC_ALL=C: byte semantics everywhere, which is how this code is
47
+ # written. Under a UTF-8 locale, macOS's awk is still byte-based for substr()
48
+ # and length(), but a regex match against half a multi-byte character is
49
+ # fatal ("towc: multibyte conversion failure"). 2.15.1 died that way on
50
+ # "## 2026-08-04(三)" and fell back to the file's tail (2026-09-25).
46
51
  # Every fork costs tens of milliseconds on Windows Git Bash, so awk strips the
47
52
  # CRs itself and its output is parsed with bash builtins: this runs at the
48
53
  # start of every session.
@@ -83,7 +88,7 @@ function skip() {
83
88
  STATUS=""; STATUS_NOTE=""
84
89
  if [ -f "docs/progress.md" ]; then
85
90
  # One pass. Emits "@key=value" lines, then the capped body as "|line".
86
- PROGRESS=$(awk "$AWK_LIB"'
91
+ PROGRESS=$(LC_ALL=C awk "$AWK_LIB"'
87
92
  BEGIN { lines = 15 }
88
93
  { sub(/\r$/, "") }
89
94
  /<!--[ \t]*cc-discipline:[ \t]*status-lines=/ {
@@ -152,7 +157,7 @@ if [ -f "docs/todo.md" ]; then
152
157
  # One pass: the Now body (capped at 20), and Later's top-level items. An open
153
158
  # item is a top-level bullet that isn't ticked ("- [ ] x" or "- x", not
154
159
  # "- [x] x"); indented lines belong to the item above them.
155
- TODO=$(awk "$AWK_LIB"'
160
+ TODO=$(LC_ALL=C awk "$AWK_LIB"'
156
161
  function close_item() { if (item && !cond) nocond++; item = 0 }
157
162
  { sub(/\r$/, "") }
158
163
  { k = skip() }