@michengai/dsh-pua 0.3.8

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 (170) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +38 -0
  4. package/README.md +150 -0
  5. package/assets/pua/command-again.md +23 -0
  6. package/assets/pua/command-done-check.md +21 -0
  7. package/assets/pua/command-evidence.md +18 -0
  8. package/assets/pua/flavors.md +388 -0
  9. package/assets/pua/methodology-alibaba.md +33 -0
  10. package/assets/pua/methodology-amazon.md +42 -0
  11. package/assets/pua/methodology-apple.md +42 -0
  12. package/assets/pua/methodology-baidu.md +33 -0
  13. package/assets/pua/methodology-bytedance.md +41 -0
  14. package/assets/pua/methodology-ding.md +75 -0
  15. package/assets/pua/methodology-huawei.md +95 -0
  16. package/assets/pua/methodology-jd.md +42 -0
  17. package/assets/pua/methodology-meituan.md +41 -0
  18. package/assets/pua/methodology-microsoft.md +138 -0
  19. package/assets/pua/methodology-netflix.md +41 -0
  20. package/assets/pua/methodology-pinduoduo.md +33 -0
  21. package/assets/pua/methodology-tencent.md +41 -0
  22. package/assets/pua/methodology-tesla.md +42 -0
  23. package/assets/pua/methodology-xiaomi.md +42 -0
  24. package/assets/pua/upstream/agents/cto-p10.md +87 -0
  25. package/assets/pua/upstream/agents/pua-action-executor.md +60 -0
  26. package/assets/pua/upstream/agents/pua-policy-guardian.md +54 -0
  27. package/assets/pua/upstream/agents/pua-self-reviewer.md +62 -0
  28. package/assets/pua/upstream/agents/pua-verifier.md +61 -0
  29. package/assets/pua/upstream/agents/senior-engineer-p7.md +116 -0
  30. package/assets/pua/upstream/agents/tech-lead-p9.md +97 -0
  31. package/assets/pua/upstream/commands/again.md +23 -0
  32. package/assets/pua/upstream/commands/cancel-pua-loop.md +62 -0
  33. package/assets/pua/upstream/commands/ding.md +25 -0
  34. package/assets/pua/upstream/commands/done-check.md +21 -0
  35. package/assets/pua/upstream/commands/evidence.md +18 -0
  36. package/assets/pua/upstream/commands/flavor.md +6 -0
  37. package/assets/pua/upstream/commands/kpi.md +5 -0
  38. package/assets/pua/upstream/commands/mama.md +5 -0
  39. package/assets/pua/upstream/commands/off.md +41 -0
  40. package/assets/pua/upstream/commands/offline.md +38 -0
  41. package/assets/pua/upstream/commands/on.md +15 -0
  42. package/assets/pua/upstream/commands/p10.md +5 -0
  43. package/assets/pua/upstream/commands/p7.md +5 -0
  44. package/assets/pua/upstream/commands/p9.md +5 -0
  45. package/assets/pua/upstream/commands/pro.md +5 -0
  46. package/assets/pua/upstream/commands/pua-loop.md +5 -0
  47. package/assets/pua/upstream/commands/pua.md +44 -0
  48. package/assets/pua/upstream/commands/reap-orphans.md +68 -0
  49. package/assets/pua/upstream/commands/survey.md +9 -0
  50. package/assets/pua/upstream/commands/team-status.md +56 -0
  51. package/assets/pua/upstream/commands/teardown-all.md +80 -0
  52. package/assets/pua/upstream/commands/yes.md +5 -0
  53. package/assets/pua/upstream/hooks/checkpoint-save.sh +56 -0
  54. package/assets/pua/upstream/hooks/failure-detector.sh +266 -0
  55. package/assets/pua/upstream/hooks/flavor-helper.sh +300 -0
  56. package/assets/pua/upstream/hooks/frustration-trigger.sh +61 -0
  57. package/assets/pua/upstream/hooks/hooks.json +114 -0
  58. package/assets/pua/upstream/hooks/integrity-guard.sh +494 -0
  59. package/assets/pua/upstream/hooks/pua-loop-hook.sh +360 -0
  60. package/assets/pua/upstream/hooks/runtime-state.py +460 -0
  61. package/assets/pua/upstream/hooks/sanitize-session.sh +165 -0
  62. package/assets/pua/upstream/hooks/session-restore.sh +189 -0
  63. package/assets/pua/upstream/hooks/stop-feedback.sh +51 -0
  64. package/assets/pua/upstream/hooks/subagent-teardown.sh +56 -0
  65. package/assets/pua/upstream/skills/ding/SKILL.md +83 -0
  66. package/assets/pua/upstream/skills/ding/references/ding-reminders.md +77 -0
  67. package/assets/pua/upstream/skills/ding/references/methodology-ding.md +75 -0
  68. package/assets/pua/upstream/skills/mama/SKILL.md +117 -0
  69. package/assets/pua/upstream/skills/p10/SKILL.md +13 -0
  70. package/assets/pua/upstream/skills/p7/SKILL.md +13 -0
  71. package/assets/pua/upstream/skills/p9/SKILL.md +15 -0
  72. package/assets/pua/upstream/skills/pro/SKILL.md +69 -0
  73. package/assets/pua/upstream/skills/pua/SKILL.md +438 -0
  74. package/assets/pua/upstream/skills/pua/references/agent-team.md +110 -0
  75. package/assets/pua/upstream/skills/pua/references/de-escalation-protocol.md +134 -0
  76. package/assets/pua/upstream/skills/pua/references/ding-reminders.md +77 -0
  77. package/assets/pua/upstream/skills/pua/references/display-protocol.md +63 -0
  78. package/assets/pua/upstream/skills/pua/references/evolution-protocol.md +187 -0
  79. package/assets/pua/upstream/skills/pua/references/flavors.md +388 -0
  80. package/assets/pua/upstream/skills/pua/references/harness-governance.md +159 -0
  81. package/assets/pua/upstream/skills/pua/references/methodology-alibaba.md +33 -0
  82. package/assets/pua/upstream/skills/pua/references/methodology-amazon.md +42 -0
  83. package/assets/pua/upstream/skills/pua/references/methodology-apple.md +42 -0
  84. package/assets/pua/upstream/skills/pua/references/methodology-baidu.md +33 -0
  85. package/assets/pua/upstream/skills/pua/references/methodology-bytedance.md +41 -0
  86. package/assets/pua/upstream/skills/pua/references/methodology-ding.md +75 -0
  87. package/assets/pua/upstream/skills/pua/references/methodology-huawei.md +95 -0
  88. package/assets/pua/upstream/skills/pua/references/methodology-jd.md +42 -0
  89. package/assets/pua/upstream/skills/pua/references/methodology-meituan.md +41 -0
  90. package/assets/pua/upstream/skills/pua/references/methodology-microsoft.md +138 -0
  91. package/assets/pua/upstream/skills/pua/references/methodology-netflix.md +41 -0
  92. package/assets/pua/upstream/skills/pua/references/methodology-pinduoduo.md +33 -0
  93. package/assets/pua/upstream/skills/pua/references/methodology-router.md +81 -0
  94. package/assets/pua/upstream/skills/pua/references/methodology-tencent.md +41 -0
  95. package/assets/pua/upstream/skills/pua/references/methodology-tesla.md +42 -0
  96. package/assets/pua/upstream/skills/pua/references/methodology-xiaomi.md +42 -0
  97. package/assets/pua/upstream/skills/pua/references/p10-protocol.md +127 -0
  98. package/assets/pua/upstream/skills/pua/references/p7-protocol.md +250 -0
  99. package/assets/pua/upstream/skills/pua/references/p9-protocol.md +266 -0
  100. package/assets/pua/upstream/skills/pua/references/platform.md +126 -0
  101. package/assets/pua/upstream/skills/pua/references/runtime-contract.md +65 -0
  102. package/assets/pua/upstream/skills/pua/references/survey.md +292 -0
  103. package/assets/pua/upstream/skills/pua/references/teardown-protocol.md +195 -0
  104. package/assets/pua/upstream/skills/pua-en/SKILL.md +344 -0
  105. package/assets/pua/upstream/skills/pua-ja/SKILL.md +378 -0
  106. package/assets/pua/upstream/skills/pua-loop/SKILL.md +162 -0
  107. package/assets/pua/upstream/skills/shot/SKILL.md +449 -0
  108. package/assets/pua/upstream/skills/yes/SKILL.md +76 -0
  109. package/assets/pua/upstream.json +637 -0
  110. package/assets/screenshots/pua-global-settings.png +0 -0
  111. package/assets/screenshots/pua-session-settings.png +0 -0
  112. package/cordis.patch.yml +5 -0
  113. package/lib/args.d.ts +34 -0
  114. package/lib/args.js +149 -0
  115. package/lib/args.js.map +1 -0
  116. package/lib/client-refresh.d.ts +6 -0
  117. package/lib/client-refresh.js +41 -0
  118. package/lib/client-refresh.js.map +1 -0
  119. package/lib/client.d.ts +30 -0
  120. package/lib/client.js +68 -0
  121. package/lib/client.js.map +7 -0
  122. package/lib/command.d.ts +18 -0
  123. package/lib/command.js +118 -0
  124. package/lib/command.js.map +1 -0
  125. package/lib/configuration.d.ts +89 -0
  126. package/lib/configuration.js +31 -0
  127. package/lib/configuration.js.map +1 -0
  128. package/lib/content.d.ts +14 -0
  129. package/lib/content.js +51 -0
  130. package/lib/content.js.map +1 -0
  131. package/lib/flavors.d.ts +82 -0
  132. package/lib/flavors.js +30 -0
  133. package/lib/flavors.js.map +1 -0
  134. package/lib/hook-content.d.ts +14 -0
  135. package/lib/hook-content.js +63 -0
  136. package/lib/hook-content.js.map +1 -0
  137. package/lib/index.d.ts +21 -0
  138. package/lib/index.js +75 -0
  139. package/lib/index.js.map +1 -0
  140. package/lib/remote-contract.d.ts +148 -0
  141. package/lib/remote-contract.js +20 -0
  142. package/lib/remote-contract.js.map +1 -0
  143. package/lib/remote.d.ts +20 -0
  144. package/lib/remote.js +133 -0
  145. package/lib/remote.js.map +1 -0
  146. package/lib/review.d.ts +4 -0
  147. package/lib/review.js +72 -0
  148. package/lib/review.js.map +1 -0
  149. package/lib/runtime.d.ts +61 -0
  150. package/lib/runtime.js +519 -0
  151. package/lib/runtime.js.map +1 -0
  152. package/lib/session-compat.d.ts +3 -0
  153. package/lib/session-compat.js +9 -0
  154. package/lib/session-compat.js.map +1 -0
  155. package/lib/settings.d.ts +46 -0
  156. package/lib/settings.js +44 -0
  157. package/lib/settings.js.map +1 -0
  158. package/lib/source.d.ts +10 -0
  159. package/lib/source.js +32 -0
  160. package/lib/source.js.map +1 -0
  161. package/lib/state.d.ts +36 -0
  162. package/lib/state.js +187 -0
  163. package/lib/state.js.map +1 -0
  164. package/lib/terminal-observation.d.ts +4 -0
  165. package/lib/terminal-observation.js +12 -0
  166. package/lib/terminal-observation.js.map +1 -0
  167. package/lib/tool-order.d.ts +9 -0
  168. package/lib/tool-order.js +82 -0
  169. package/lib/tool-order.js.map +1 -0
  170. package/package.json +172 -0
@@ -0,0 +1,360 @@
1
+ #!/bin/bash
2
+
3
+ # PUA Loop Stop Hook — with autoresearch-style Gate Protocol
4
+ # Prevents session exit when a pua-loop is active
5
+ # Feeds Claude's output back as input to continue the loop
6
+ #
7
+ # Gate Protocol (inspired by autoresearch):
8
+ # Phase 1: Claude self-reports via <promise> tag (in-prompt)
9
+ # Phase 2: Hook runs verify_command independently (Oracle Isolation)
10
+ # If Phase 2 fails → promise REJECTED → loop continues
11
+ #
12
+ # Adapted from Ralph Wiggum by Anthropic (MIT License)
13
+ # https://github.com/anthropics/claude-code/tree/main/plugins/ralph-wiggum
14
+
15
+ set -euo pipefail
16
+ command -v jq &>/dev/null || { echo "jq not found, skipping" >&2; exit 0; }
17
+
18
+ # Portable timeout wrapper. macOS does not ship GNU `timeout`; Homebrew may
19
+ # provide `gtimeout`, and Perl is available by default on macOS/Linux.
20
+ run_with_timeout() {
21
+ local seconds="$1"
22
+ shift
23
+ if command -v timeout >/dev/null 2>&1; then
24
+ timeout "$seconds" "$@"
25
+ elif command -v gtimeout >/dev/null 2>&1; then
26
+ gtimeout "$seconds" "$@"
27
+ else
28
+ perl -e '
29
+ my $seconds = shift @ARGV;
30
+ $SIG{ALRM} = sub { exit 124 };
31
+ alarm($seconds);
32
+ exec @ARGV;
33
+ ' "$seconds" "$@"
34
+ fi
35
+ }
36
+
37
+ HOOK_INPUT=$(cat)
38
+
39
+ # ═══════════════════════════════════════════════════════════════
40
+ # Gate 0 — Defensive Subagent Isolation
41
+ #
42
+ # Claude Code 官方实际:Stop hook 仅主会话触发,subagent 走独立的
43
+ # SubagentStop 事件注册;`parent_session_id` 字段在 Stop payload 中
44
+ # 不存在。以下判断在当前版本是 dead code,**保留是防御性编程**——
45
+ # 若未来调度行为变化,这层 gate 能兜住 regression。
46
+ # jq 失败(非法 JSON)时返回空字符串不触发 set -e,等价于 fail-open
47
+ # 但后续 state 文件解析会再次校验,综合不可劫持。
48
+ # ═══════════════════════════════════════════════════════════════
49
+ HOOK_EVENT=$(echo "$HOOK_INPUT" | jq -r '.hook_event_name // ""' 2>/dev/null || echo "")
50
+ PARENT_SESSION=$(echo "$HOOK_INPUT" | jq -r '.parent_session_id // ""' 2>/dev/null || echo "")
51
+ if [[ "$HOOK_EVENT" == "SubagentStop" ]] || [[ -n "$PARENT_SESSION" ]]; then
52
+ exit 0
53
+ fi
54
+
55
+ # ═══════════════════════════════════════════════════════════════
56
+ # State file resolution (v3.2)
57
+ # 用 cwd 哈希命名:$HOME/.claude/pua/loop-<hash>.md(每个项目目录独立)
58
+ # 兼容 v3.1 单文件 loop-active.md(检查 started_cwd 匹配)
59
+ # 兼容 legacy .claude/pua-loop.local.md
60
+ # ═══════════════════════════════════════════════════════════════
61
+ HOOK_SESSION_ID=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""' 2>/dev/null || echo "")
62
+ PUA_DIR="${HOME}/.claude/pua"
63
+ CWD_HASH=$(printf '%s' "$(pwd)" | md5sum 2>/dev/null | cut -c1-8 || printf '%s' "$(pwd)" | md5 2>/dev/null | cut -c1-8 || echo "default")
64
+ ABS_STATE_FILE="${PUA_DIR}/loop-${CWD_HASH}.md"
65
+ LEGACY_ABS_STATE_FILE="${PUA_DIR}/loop-active.md"
66
+ LEGACY_STATE_FILE=".claude/pua-loop.local.md"
67
+
68
+ if [[ -f "$ABS_STATE_FILE" ]]; then
69
+ RALPH_STATE_FILE="$ABS_STATE_FILE"
70
+ elif [[ -f "$LEGACY_ABS_STATE_FILE" ]]; then
71
+ # v3.1 兼容:旧版单文件,检查 started_cwd 是否匹配当前目录
72
+ LEGACY_CWD=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$LEGACY_ABS_STATE_FILE" | grep '^started_cwd:' | sed 's/started_cwd: *//' | sed 's/^"\(.*\)"$/\1/' || true)
73
+ if [[ "$LEGACY_CWD" == "$(pwd)" ]] || [[ -z "$LEGACY_CWD" ]]; then
74
+ RALPH_STATE_FILE="$LEGACY_ABS_STATE_FILE"
75
+ elif [[ -f "$LEGACY_STATE_FILE" ]]; then
76
+ RALPH_STATE_FILE="$LEGACY_STATE_FILE"
77
+ else
78
+ exit 0
79
+ fi
80
+ elif [[ -f "$LEGACY_STATE_FILE" ]]; then
81
+ RALPH_STATE_FILE="$LEGACY_STATE_FILE"
82
+ else
83
+ exit 0
84
+ fi
85
+
86
+ # ═══════════════════════════════════════════════════════════════
87
+ # Stale lock detection
88
+ # mtime > 30min 视为孤儿 state(上次会话崩溃、subagent 遗留),清理退出。
89
+ # macOS 用 stat -f %m,Linux 用 stat -c %Y,兜底 0。
90
+ # ═══════════════════════════════════════════════════════════════
91
+ MTIME=$(stat -f %m "$RALPH_STATE_FILE" 2>/dev/null || stat -c %Y "$RALPH_STATE_FILE" 2>/dev/null || echo 0)
92
+ NOW=$(date +%s)
93
+ if [[ "$MTIME" =~ ^[0-9]+$ ]] && [[ $((NOW - MTIME)) -gt 1800 ]]; then
94
+ echo "🧹 PUA Loop: state file stale (>30min idle), reaping orphan" >&2
95
+ echo "{\"status\":\"orphan_reaped\",\"state_file\":\"$RALPH_STATE_FILE\",\"age_sec\":$((NOW - MTIME)),\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> "${PUA_DIR}/loop-history.jsonl" 2>/dev/null || \
96
+ echo "{\"status\":\"orphan_reaped\",\"state_file\":\"$RALPH_STATE_FILE\",\"age_sec\":$((NOW - MTIME)),\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
97
+ rm -f "$RALPH_STATE_FILE"
98
+ exit 0
99
+ fi
100
+
101
+ # Normalize CRLF
102
+ TEMP_NORM="${RALPH_STATE_FILE}.norm.$$"
103
+ tr -d '\r' < "$RALPH_STATE_FILE" > "$TEMP_NORM" && mv "$TEMP_NORM" "$RALPH_STATE_FILE"
104
+
105
+ # Parse frontmatter
106
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE" | tr -d '\r')
107
+ LOOP_ACTIVE=$(echo "$FRONTMATTER" | grep '^active:' | sed 's/active: *//' || true)
108
+ ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//' || true)
109
+ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//' || true)
110
+ COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/' || true)
111
+ VERIFY_CMD=$(echo "$FRONTMATTER" | grep '^verify_command:' | sed 's/verify_command: *//' | sed 's/^"\(.*\)"$/\1/' || true)
112
+ PROMISE_REJECTIONS=$(echo "$FRONTMATTER" | grep '^promise_rejections:' | sed 's/promise_rejections: *//' || echo "0")
113
+
114
+ # Validate numeric fields
115
+ [[ ! "$PROMISE_REJECTIONS" =~ ^[0-9]+$ ]] && PROMISE_REJECTIONS=0
116
+
117
+ # Check if loop is paused
118
+ if [[ "$LOOP_ACTIVE" == "false" ]]; then
119
+ exit 0
120
+ fi
121
+
122
+ # Session isolation
123
+ STATE_SESSION=$(echo "$FRONTMATTER" | grep '^session_id:' | sed 's/session_id: *//' || true)
124
+ HOOK_SESSION=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""')
125
+
126
+ if [[ -z "$STATE_SESSION" ]] && [[ "$HOOK_SESSION" != "" ]]; then
127
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
128
+ sed "s/^session_id:.*/session_id: $HOOK_SESSION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
129
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
130
+ STATE_SESSION="$HOOK_SESSION"
131
+ fi
132
+
133
+ if [[ -n "$STATE_SESSION" ]] && [[ "$STATE_SESSION" != "$HOOK_SESSION" ]]; then
134
+ exit 0
135
+ fi
136
+
137
+ # Validate iteration
138
+ if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
139
+ echo "⚠️ PUA Loop: State file corrupted (iteration: '$ITERATION')" >&2
140
+ rm "$RALPH_STATE_FILE"
141
+ exit 0
142
+ fi
143
+
144
+ if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then
145
+ echo "⚠️ PUA Loop: State file corrupted (max_iterations: '$MAX_ITERATIONS')" >&2
146
+ rm "$RALPH_STATE_FILE"
147
+ exit 0
148
+ fi
149
+
150
+ # Check max iterations
151
+ if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then
152
+ echo "🛑 PUA Loop: Max iterations ($MAX_ITERATIONS) reached."
153
+ echo "{\"iteration\":$ITERATION,\"status\":\"max_reached\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
154
+ rm "$RALPH_STATE_FILE"
155
+ exit 0
156
+ fi
157
+
158
+ # Get transcript
159
+ TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path')
160
+
161
+ if [[ ! -f "$TRANSCRIPT_PATH" ]]; then
162
+ echo "⚠️ PUA Loop: Transcript not found" >&2
163
+ rm "$RALPH_STATE_FILE"
164
+ exit 0
165
+ fi
166
+
167
+ if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
168
+ echo "⚠️ PUA Loop: No assistant messages in transcript" >&2
169
+ rm "$RALPH_STATE_FILE"
170
+ exit 0
171
+ fi
172
+
173
+ # Extract last assistant text
174
+ LAST_LINES=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -n 100) || true
175
+ if [[ -z "$LAST_LINES" ]]; then
176
+ rm "$RALPH_STATE_FILE"
177
+ exit 0
178
+ fi
179
+
180
+ set +e
181
+ LAST_OUTPUT=$(echo "$LAST_LINES" | jq -rs '
182
+ map(.message.content[]? | select(.type == "text") | .text) | last // ""
183
+ ' 2>&1)
184
+ JQ_EXIT=$?
185
+ set -e
186
+
187
+ if [[ $JQ_EXIT -ne 0 ]]; then
188
+ echo "⚠️ PUA Loop: JSON parse failed" >&2
189
+ rm "$RALPH_STATE_FILE"
190
+ exit 0
191
+ fi
192
+
193
+ # ─── Signal detection (priority: abort > pause > promise) ───
194
+
195
+ # Check <loop-abort>
196
+ ABORT_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -ne 'if (/<loop-abort>(.*?)<\/loop-abort>/s) { $t=$1; $t=~s/^\s+|\s+$//g; print $t }' 2>/dev/null || echo "")
197
+ if [[ -n "$ABORT_TEXT" ]]; then
198
+ echo "🛑 PUA Loop: <loop-abort> received. Reason: $ABORT_TEXT"
199
+ echo "{\"iteration\":$ITERATION,\"status\":\"abort\",\"reason\":\"$(echo "$ABORT_TEXT" | head -1 | tr '"' "'")\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
200
+ rm "$RALPH_STATE_FILE"
201
+ exit 0
202
+ fi
203
+
204
+ # Check <loop-pause>
205
+ PAUSE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -ne 'if (/<loop-pause>(.*?)<\/loop-pause>/s) { $t=$1; $t=~s/^\s+|\s+$//g; print $t }' 2>/dev/null || echo "")
206
+ if [[ -n "$PAUSE_TEXT" ]]; then
207
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
208
+ sed "s/^active:.*/active: false/" "$RALPH_STATE_FILE" | \
209
+ sed "s/^session_id:.*/session_id: /" > "$TEMP_FILE"
210
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
211
+ echo ""
212
+ echo "⏸️ PUA Loop paused (iteration $ITERATION)"
213
+ echo " Needs: $PAUSE_TEXT"
214
+ echo " State saved. Resume by reopening Claude Code."
215
+ echo "{\"iteration\":$ITERATION,\"status\":\"pause\",\"reason\":\"$(echo "$PAUSE_TEXT" | head -1 | tr '"' "'")\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
216
+ exit 0
217
+ fi
218
+
219
+ # ─── Promise detection + Oracle Gate ───
220
+
221
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
222
+ PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
223
+
224
+ if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
225
+
226
+ # ─── Gate Phase 2: Oracle Verification ───
227
+ if [[ -n "$VERIFY_CMD" ]] && [[ "$VERIFY_CMD" != "null" ]]; then
228
+
229
+ # Run verify command with 120s timeout (Oracle Isolation)
230
+ set +e
231
+ VERIFY_OUTPUT=$(run_with_timeout 120 bash -c "$VERIFY_CMD" 2>&1)
232
+ VERIFY_EXIT=$?
233
+ set -e
234
+
235
+ if [[ $VERIFY_EXIT -ne 0 ]]; then
236
+ # ═══ PROMISE REJECTED ═══
237
+ PROMISE_REJECTIONS=$((PROMISE_REJECTIONS + 1))
238
+
239
+ # Log rejection with verify output tail
240
+ VERIFY_TAIL=$(echo "$VERIFY_OUTPUT" | tail -5 | tr '\n' ' ' | cut -c1-200)
241
+ echo "{\"iteration\":$ITERATION,\"status\":\"promise_rejected\",\"verify_exit\":$VERIFY_EXIT,\"rejections\":$PROMISE_REJECTIONS,\"verify_tail\":\"$(echo "$VERIFY_TAIL" | tr '"' "'")\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
242
+
243
+ # Update state file: increment iteration + promise_rejections
244
+ NEXT_ITERATION=$((ITERATION + 1))
245
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
246
+ sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" | \
247
+ sed "s/^promise_rejections: .*/promise_rejections: $PROMISE_REJECTIONS/" > "$TEMP_FILE"
248
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
249
+
250
+ # Extract prompt
251
+ PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")
252
+ if [[ -z "$PROMPT_TEXT" ]]; then
253
+ echo "⚠️ PUA Loop: State file corrupted" >&2
254
+ rm "$RALPH_STATE_FILE"
255
+ exit 0
256
+ fi
257
+
258
+ # Build rejection system message with verify output
259
+ VERIFY_DISPLAY=$(echo "$VERIFY_OUTPUT" | tail -10)
260
+ REJECTION_MSG="🚫 PROMISE 被 Oracle 拒绝!verify_command 退出码 ${VERIFY_EXIT}(连续第 ${PROMISE_REJECTIONS} 次拒绝)"
261
+
262
+ # Stall escalation on repeated rejections
263
+ if [[ $PROMISE_REJECTIONS -ge 5 ]]; then
264
+ REJECTION_MSG="$REJECTION_MSG | ⚠️ 已连续 ${PROMISE_REJECTIONS} 次虚假 promise!你在解决错误的问题。退回到需求本身重新理解。读 .claude/pua-loop-history.jsonl 了解失败模式。"
265
+ elif [[ $PROMISE_REJECTIONS -ge 3 ]]; then
266
+ REJECTION_MSG="$REJECTION_MSG | ⚠️ 连续 ${PROMISE_REJECTIONS} 次验证失败。REASSESS:重读验证输出、搜索相关源码、列 3 个不同假设再行动。不要再用同样的方法。"
267
+ fi
268
+
269
+ SYSTEM_MSG="$REJECTION_MSG | 验证输出(tail): $VERIFY_DISPLAY"
270
+
271
+ jq -n \
272
+ --arg prompt "$PROMPT_TEXT" \
273
+ --arg msg "$SYSTEM_MSG" \
274
+ '{"decision":"block","reason":$prompt,"systemMessage":$msg}'
275
+ exit 0
276
+ fi
277
+
278
+ # Verify PASSED — Oracle confirms completion
279
+ echo "✅ PUA Loop: <promise> verified by Oracle (exit 0)"
280
+ else
281
+ # No verify command — honor system
282
+ echo "✅ PUA Loop: <promise> accepted (no Oracle configured)"
283
+ fi
284
+
285
+ # ═══ PROMISE ACCEPTED ═══
286
+ echo "{\"iteration\":$ITERATION,\"status\":\"complete\",\"promise_rejections\":$PROMISE_REJECTIONS,\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
287
+ rm "$RALPH_STATE_FILE"
288
+ exit 0
289
+ fi
290
+ fi
291
+
292
+ # ─── Not complete — continue loop ───
293
+
294
+ NEXT_ITERATION=$((ITERATION + 1))
295
+
296
+ # Log continuation
297
+ echo "{\"iteration\":$ITERATION,\"status\":\"continue\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" >> .claude/pua-loop-history.jsonl 2>/dev/null || true
298
+
299
+ # Extract prompt
300
+ PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")
301
+
302
+ if [[ -z "$PROMPT_TEXT" ]]; then
303
+ echo "⚠️ PUA Loop: State file corrupted (no prompt)" >&2
304
+ rm "$RALPH_STATE_FILE"
305
+ exit 0
306
+ fi
307
+
308
+ # Update iteration
309
+ TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
310
+ sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
311
+ mv "$TEMP_FILE" "$RALPH_STATE_FILE"
312
+
313
+ # ─── Pressure system ───
314
+
315
+ SIGNAL_HINT="终止用 <loop-abort>原因</loop-abort>,需人工介入用 <loop-pause>需要什么</loop-pause>"
316
+
317
+ # Pressure escalation (no artificial cap)
318
+ if [[ $NEXT_ITERATION -le 3 ]]; then
319
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮迭代,稳步推进。"
320
+ elif [[ $NEXT_ITERATION -le 7 ]]; then
321
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮了还没搞定?换方案,别原地打转。"
322
+ elif [[ $NEXT_ITERATION -le 15 ]]; then
323
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮。底层逻辑到底是什么?先 git log 看自己做了什么,读 .claude/pua-loop-history.jsonl 了解迭代历史。"
324
+ elif [[ $NEXT_ITERATION -le 30 ]]; then
325
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮。3.25 的边缘了。穷尽了吗?git diff 确认没在重复。"
326
+ elif [[ $NEXT_ITERATION -le 50 ]]; then
327
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮。停下来重新审视:问题的根因到底是什么?用完全不同的思路。"
328
+ elif [[ $NEXT_ITERATION -le 100 ]]; then
329
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮。马拉松模式。退回去从需求本身重新质疑(The Algorithm: question the requirement)。"
330
+ else
331
+ PUA_PRESSURE="▎ 第 ${NEXT_ITERATION} 轮。超长迭代。如果任务真的无法在 loop 内完成,用 <loop-abort> 诚实报告。"
332
+ fi
333
+
334
+ # Stall warning from promise rejections (autoresearch-style)
335
+ STALL_MSG=""
336
+ if [[ $PROMISE_REJECTIONS -ge 5 ]]; then
337
+ STALL_MSG=" | ⚠️ Oracle 已连续拒绝 ${PROMISE_REJECTIONS} 次。你在解决错误的问题。读 history.jsonl,用完全不同的方案。"
338
+ elif [[ $PROMISE_REJECTIONS -ge 3 ]]; then
339
+ STALL_MSG=" | ⚠️ Oracle 连续拒绝 ${PROMISE_REJECTIONS} 次。REASSESS:读验证输出,列 3 个不同假设。"
340
+ elif [[ $PROMISE_REJECTIONS -ge 1 ]]; then
341
+ STALL_MSG=" | 上次 promise 被 Oracle 拒绝(共 ${PROMISE_REJECTIONS} 次)。修复验证问题后再声称完成。"
342
+ fi
343
+
344
+ # Build system message
345
+ if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
346
+ SYSTEM_MSG="${PUA_PRESSURE}${STALL_MSG} | 完成后输出 <promise>$COMPLETION_PROMISE</promise> (ONLY when TRUE) | $SIGNAL_HINT"
347
+ else
348
+ SYSTEM_MSG="${PUA_PRESSURE}${STALL_MSG} | $SIGNAL_HINT"
349
+ fi
350
+
351
+ jq -n \
352
+ --arg prompt "$PROMPT_TEXT" \
353
+ --arg msg "$SYSTEM_MSG" \
354
+ '{
355
+ "decision": "block",
356
+ "reason": $prompt,
357
+ "systemMessage": $msg
358
+ }'
359
+
360
+ exit 0