@walwal-harness/cli 7.1.34 → 7.1.37

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.
@@ -26,6 +26,28 @@ LAUNCH_AGENTS_DIR="$HOME/Library/LaunchAgents"
26
26
  PLIST_NAME="com.walwal.harness-wake"
27
27
  PLIST_PATH="$LAUNCH_AGENTS_DIR/$PLIST_NAME.plist"
28
28
 
29
+ launch_domain() {
30
+ echo "gui/$(id -u)"
31
+ }
32
+
33
+ launch_is_loaded() {
34
+ local domain
35
+ domain="$(launch_domain)"
36
+ launchctl print "$domain/$PLIST_NAME" >/dev/null 2>&1
37
+ }
38
+
39
+ launch_unload() {
40
+ local domain
41
+ domain="$(launch_domain)"
42
+ launchctl bootout "$domain" "$PLIST_PATH" >/dev/null 2>&1 || launchctl unload "$PLIST_PATH" 2>/dev/null || true
43
+ }
44
+
45
+ launch_load() {
46
+ local domain
47
+ domain="$(launch_domain)"
48
+ launchctl bootstrap "$domain" "$PLIST_PATH" >/dev/null 2>&1 || launchctl load "$PLIST_PATH" 2>/dev/null
49
+ }
50
+
29
51
  # 템플릿 위치 후보 — walwal-harness 패키지 안일 수도, 프로젝트 안일 수도.
30
52
  detect_template() {
31
53
  local candidates=(
@@ -39,8 +61,8 @@ detect_template() {
39
61
  }
40
62
 
41
63
  ensure_dirs() {
42
- mkdir -p "$HARNESS_DIR" "$LOG_DIR" "$LAUNCH_AGENTS_DIR"
43
- touch "$PROJECTS_LIST"
64
+ mkdir -p "$HARNESS_DIR" "$LOG_DIR" "$LAUNCH_AGENTS_DIR" || return 1
65
+ touch "$PROJECTS_LIST" || return 1
44
66
  }
45
67
 
46
68
  abs_path() {
@@ -53,19 +75,32 @@ abs_path() {
53
75
  }
54
76
 
55
77
  cmd_add() {
56
- ensure_dirs
78
+ ensure_dirs || {
79
+ echo "[wake] ERROR: cannot prepare $HARNESS_DIR" >&2
80
+ return 1
81
+ }
57
82
  local project
58
83
  project=$(abs_path "$1")
59
84
  if grep -Fxq "$project" "$PROJECTS_LIST" 2>/dev/null; then
60
85
  echo "[wake] already registered: $project"
61
86
  return 0
62
87
  fi
63
- echo "$project" >> "$PROJECTS_LIST"
88
+ echo "$project" >> "$PROJECTS_LIST" || {
89
+ echo "[wake] ERROR: cannot write $PROJECTS_LIST" >&2
90
+ return 1
91
+ }
92
+ if ! grep -Fxq "$project" "$PROJECTS_LIST" 2>/dev/null; then
93
+ echo "[wake] ERROR: registration did not persist: $project" >&2
94
+ return 1
95
+ fi
64
96
  echo "[wake] added: $project"
65
97
  }
66
98
 
67
99
  cmd_remove() {
68
- ensure_dirs
100
+ ensure_dirs || {
101
+ echo "[wake] ERROR: cannot prepare $HARNESS_DIR" >&2
102
+ return 1
103
+ }
69
104
  local project
70
105
  project=$(abs_path "$1")
71
106
  if ! grep -Fxq "$project" "$PROJECTS_LIST" 2>/dev/null; then
@@ -80,7 +115,10 @@ cmd_remove() {
80
115
  }
81
116
 
82
117
  cmd_list() {
83
- ensure_dirs
118
+ ensure_dirs || {
119
+ echo "[wake] ERROR: cannot prepare $HARNESS_DIR" >&2
120
+ return 1
121
+ }
84
122
  if [ ! -s "$PROJECTS_LIST" ]; then
85
123
  echo "(empty)"
86
124
  return 0
@@ -89,28 +127,41 @@ cmd_list() {
89
127
  }
90
128
 
91
129
  cmd_status() {
130
+ ensure_dirs || {
131
+ echo "[wake] ERROR: cannot prepare $HARNESS_DIR" >&2
132
+ return 1
133
+ }
134
+ local loaded=1
92
135
  if [ -f "$PLIST_PATH" ]; then
93
136
  echo "[wake] plist: $PLIST_PATH (exists)"
94
137
  else
95
138
  echo "[wake] plist: $PLIST_PATH (NOT installed)"
96
139
  fi
97
- if launchctl list 2>/dev/null | grep -q "$PLIST_NAME"; then
140
+ if launch_is_loaded; then
141
+ loaded=0
98
142
  echo "[wake] launchd: loaded"
99
- launchctl list | awk -v n="$PLIST_NAME" '$3==n {print " pid="$1" exit="$2" label="$3}'
143
+ launchctl print "$(launch_domain)/$PLIST_NAME" 2>/dev/null | awk '
144
+ /state =/ {print " " $0}
145
+ /run interval =/ {print " " $0}
146
+ /runs =/ {print " " $0}
147
+ '
100
148
  else
101
149
  echo "[wake] launchd: NOT loaded"
102
150
  fi
103
151
  echo "[wake] projects:"
104
152
  cmd_list | sed 's/^/ /'
153
+ return "$loaded"
105
154
  }
106
155
 
107
156
  cmd_install() {
108
- ensure_dirs
157
+ ensure_dirs || {
158
+ echo "[wake] ERROR: cannot prepare $HARNESS_DIR" >&2
159
+ return 1
160
+ }
109
161
 
110
162
  # 추가 인자가 있으면 add
111
- shift_count=0
112
163
  for p in "$@"; do
113
- cmd_add "$p"
164
+ cmd_add "$p" || return 1
114
165
  done
115
166
 
116
167
  local template
@@ -127,22 +178,24 @@ cmd_install() {
127
178
  "$template" > "$PLIST_PATH"
128
179
 
129
180
  # 기존 jobs 언로드 (idempotent)
130
- launchctl unload "$PLIST_PATH" 2>/dev/null || true
181
+ launch_unload
131
182
 
132
183
  # 로드
133
- if launchctl load "$PLIST_PATH" 2>/dev/null; then
184
+ local loaded=1
185
+ if launch_load; then
186
+ loaded=0
134
187
  echo "[wake] launchd loaded: $PLIST_PATH"
135
188
  else
136
189
  echo "[wake] WARN: launchctl load 실패. 다음 명령으로 직접 시도해 보세요:" >&2
137
190
  echo " launchctl load $PLIST_PATH" >&2
138
191
  fi
139
192
 
140
- cmd_status
193
+ cmd_status || return "$loaded"
141
194
  }
142
195
 
143
196
  cmd_uninstall() {
144
197
  if [ -f "$PLIST_PATH" ]; then
145
- launchctl unload "$PLIST_PATH" 2>/dev/null || true
198
+ launch_unload
146
199
  rm -f "$PLIST_PATH"
147
200
  echo "[wake] uninstalled: $PLIST_PATH"
148
201
  else
@@ -8,6 +8,7 @@ DOC_ROOT="$PROJECT_ROOT/.harness/documents"
8
8
  [ -d "$DOC_ROOT" ] || exit 0
9
9
 
10
10
  mode="${2:-text}"
11
+ scope="${3:-all}"
11
12
  violations=()
12
13
 
13
14
  has_implementation_notes() {
@@ -37,6 +38,40 @@ has_legacy_flat_worker_report() {
37
38
 
38
39
  mission_dirs=$(find "$DOC_ROOT" -type f \( -name 'ceo.md' -o -name 'coo.md' -o -name 'cdo.md' -o -name 'cto.md' -o -name 'cqo.md' -o -name 'ops.md' \) -exec dirname {} \; 2>/dev/null | sort -u)
39
40
 
41
+ mission_mtime() {
42
+ stat -f '%m' "$1" 2>/dev/null || stat -c '%Y' "$1" 2>/dev/null || echo 0
43
+ }
44
+
45
+ is_terminal_lifecycle() {
46
+ case "$1" in
47
+ closed|cancelled|superseded|complete|completed|blocked) return 0 ;;
48
+ *) return 1 ;;
49
+ esac
50
+ }
51
+
52
+ latest_active_mission=""
53
+ if [ "$scope" = "latest-active" ]; then
54
+ latest_active_mtime=0
55
+ while IFS= read -r mission_dir; do
56
+ [ -n "$mission_dir" ] || continue
57
+ state_path="$mission_dir/mission-state.json"
58
+ [ -f "$state_path" ] || continue
59
+ active=$(jq -r '.active // false' "$state_path" 2>/dev/null || echo false)
60
+ lifecycle=$(jq -r '.lifecycle // .status // "unknown"' "$state_path" 2>/dev/null || echo unknown)
61
+ if [ "$active" != "true" ] || is_terminal_lifecycle "$lifecycle"; then
62
+ continue
63
+ fi
64
+ mtime=$(mission_mtime "$mission_dir")
65
+ if [ "${mtime:-0}" -ge "${latest_active_mtime:-0}" ]; then
66
+ latest_active_mtime="$mtime"
67
+ latest_active_mission="$mission_dir"
68
+ fi
69
+ done <<EOF
70
+ $mission_dirs
71
+ EOF
72
+ mission_dirs="$latest_active_mission"
73
+ fi
74
+
40
75
  while IFS= read -r mission_dir; do
41
76
  [ -n "$mission_dir" ] || continue
42
77
  [ -d "$mission_dir" ] || continue