@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.
- package/CHANGELOG.md +152 -0
- package/LICENSE +201 -0
- package/NOTICE +38 -0
- package/README.md +150 -0
- package/assets/pua/command-again.md +23 -0
- package/assets/pua/command-done-check.md +21 -0
- package/assets/pua/command-evidence.md +18 -0
- package/assets/pua/flavors.md +388 -0
- package/assets/pua/methodology-alibaba.md +33 -0
- package/assets/pua/methodology-amazon.md +42 -0
- package/assets/pua/methodology-apple.md +42 -0
- package/assets/pua/methodology-baidu.md +33 -0
- package/assets/pua/methodology-bytedance.md +41 -0
- package/assets/pua/methodology-ding.md +75 -0
- package/assets/pua/methodology-huawei.md +95 -0
- package/assets/pua/methodology-jd.md +42 -0
- package/assets/pua/methodology-meituan.md +41 -0
- package/assets/pua/methodology-microsoft.md +138 -0
- package/assets/pua/methodology-netflix.md +41 -0
- package/assets/pua/methodology-pinduoduo.md +33 -0
- package/assets/pua/methodology-tencent.md +41 -0
- package/assets/pua/methodology-tesla.md +42 -0
- package/assets/pua/methodology-xiaomi.md +42 -0
- package/assets/pua/upstream/agents/cto-p10.md +87 -0
- package/assets/pua/upstream/agents/pua-action-executor.md +60 -0
- package/assets/pua/upstream/agents/pua-policy-guardian.md +54 -0
- package/assets/pua/upstream/agents/pua-self-reviewer.md +62 -0
- package/assets/pua/upstream/agents/pua-verifier.md +61 -0
- package/assets/pua/upstream/agents/senior-engineer-p7.md +116 -0
- package/assets/pua/upstream/agents/tech-lead-p9.md +97 -0
- package/assets/pua/upstream/commands/again.md +23 -0
- package/assets/pua/upstream/commands/cancel-pua-loop.md +62 -0
- package/assets/pua/upstream/commands/ding.md +25 -0
- package/assets/pua/upstream/commands/done-check.md +21 -0
- package/assets/pua/upstream/commands/evidence.md +18 -0
- package/assets/pua/upstream/commands/flavor.md +6 -0
- package/assets/pua/upstream/commands/kpi.md +5 -0
- package/assets/pua/upstream/commands/mama.md +5 -0
- package/assets/pua/upstream/commands/off.md +41 -0
- package/assets/pua/upstream/commands/offline.md +38 -0
- package/assets/pua/upstream/commands/on.md +15 -0
- package/assets/pua/upstream/commands/p10.md +5 -0
- package/assets/pua/upstream/commands/p7.md +5 -0
- package/assets/pua/upstream/commands/p9.md +5 -0
- package/assets/pua/upstream/commands/pro.md +5 -0
- package/assets/pua/upstream/commands/pua-loop.md +5 -0
- package/assets/pua/upstream/commands/pua.md +44 -0
- package/assets/pua/upstream/commands/reap-orphans.md +68 -0
- package/assets/pua/upstream/commands/survey.md +9 -0
- package/assets/pua/upstream/commands/team-status.md +56 -0
- package/assets/pua/upstream/commands/teardown-all.md +80 -0
- package/assets/pua/upstream/commands/yes.md +5 -0
- package/assets/pua/upstream/hooks/checkpoint-save.sh +56 -0
- package/assets/pua/upstream/hooks/failure-detector.sh +266 -0
- package/assets/pua/upstream/hooks/flavor-helper.sh +300 -0
- package/assets/pua/upstream/hooks/frustration-trigger.sh +61 -0
- package/assets/pua/upstream/hooks/hooks.json +114 -0
- package/assets/pua/upstream/hooks/integrity-guard.sh +494 -0
- package/assets/pua/upstream/hooks/pua-loop-hook.sh +360 -0
- package/assets/pua/upstream/hooks/runtime-state.py +460 -0
- package/assets/pua/upstream/hooks/sanitize-session.sh +165 -0
- package/assets/pua/upstream/hooks/session-restore.sh +189 -0
- package/assets/pua/upstream/hooks/stop-feedback.sh +51 -0
- package/assets/pua/upstream/hooks/subagent-teardown.sh +56 -0
- package/assets/pua/upstream/skills/ding/SKILL.md +83 -0
- package/assets/pua/upstream/skills/ding/references/ding-reminders.md +77 -0
- package/assets/pua/upstream/skills/ding/references/methodology-ding.md +75 -0
- package/assets/pua/upstream/skills/mama/SKILL.md +117 -0
- package/assets/pua/upstream/skills/p10/SKILL.md +13 -0
- package/assets/pua/upstream/skills/p7/SKILL.md +13 -0
- package/assets/pua/upstream/skills/p9/SKILL.md +15 -0
- package/assets/pua/upstream/skills/pro/SKILL.md +69 -0
- package/assets/pua/upstream/skills/pua/SKILL.md +438 -0
- package/assets/pua/upstream/skills/pua/references/agent-team.md +110 -0
- package/assets/pua/upstream/skills/pua/references/de-escalation-protocol.md +134 -0
- package/assets/pua/upstream/skills/pua/references/ding-reminders.md +77 -0
- package/assets/pua/upstream/skills/pua/references/display-protocol.md +63 -0
- package/assets/pua/upstream/skills/pua/references/evolution-protocol.md +187 -0
- package/assets/pua/upstream/skills/pua/references/flavors.md +388 -0
- package/assets/pua/upstream/skills/pua/references/harness-governance.md +159 -0
- package/assets/pua/upstream/skills/pua/references/methodology-alibaba.md +33 -0
- package/assets/pua/upstream/skills/pua/references/methodology-amazon.md +42 -0
- package/assets/pua/upstream/skills/pua/references/methodology-apple.md +42 -0
- package/assets/pua/upstream/skills/pua/references/methodology-baidu.md +33 -0
- package/assets/pua/upstream/skills/pua/references/methodology-bytedance.md +41 -0
- package/assets/pua/upstream/skills/pua/references/methodology-ding.md +75 -0
- package/assets/pua/upstream/skills/pua/references/methodology-huawei.md +95 -0
- package/assets/pua/upstream/skills/pua/references/methodology-jd.md +42 -0
- package/assets/pua/upstream/skills/pua/references/methodology-meituan.md +41 -0
- package/assets/pua/upstream/skills/pua/references/methodology-microsoft.md +138 -0
- package/assets/pua/upstream/skills/pua/references/methodology-netflix.md +41 -0
- package/assets/pua/upstream/skills/pua/references/methodology-pinduoduo.md +33 -0
- package/assets/pua/upstream/skills/pua/references/methodology-router.md +81 -0
- package/assets/pua/upstream/skills/pua/references/methodology-tencent.md +41 -0
- package/assets/pua/upstream/skills/pua/references/methodology-tesla.md +42 -0
- package/assets/pua/upstream/skills/pua/references/methodology-xiaomi.md +42 -0
- package/assets/pua/upstream/skills/pua/references/p10-protocol.md +127 -0
- package/assets/pua/upstream/skills/pua/references/p7-protocol.md +250 -0
- package/assets/pua/upstream/skills/pua/references/p9-protocol.md +266 -0
- package/assets/pua/upstream/skills/pua/references/platform.md +126 -0
- package/assets/pua/upstream/skills/pua/references/runtime-contract.md +65 -0
- package/assets/pua/upstream/skills/pua/references/survey.md +292 -0
- package/assets/pua/upstream/skills/pua/references/teardown-protocol.md +195 -0
- package/assets/pua/upstream/skills/pua-en/SKILL.md +344 -0
- package/assets/pua/upstream/skills/pua-ja/SKILL.md +378 -0
- package/assets/pua/upstream/skills/pua-loop/SKILL.md +162 -0
- package/assets/pua/upstream/skills/shot/SKILL.md +449 -0
- package/assets/pua/upstream/skills/yes/SKILL.md +76 -0
- package/assets/pua/upstream.json +637 -0
- package/assets/screenshots/pua-global-settings.png +0 -0
- package/assets/screenshots/pua-session-settings.png +0 -0
- package/cordis.patch.yml +5 -0
- package/lib/args.d.ts +34 -0
- package/lib/args.js +149 -0
- package/lib/args.js.map +1 -0
- package/lib/client-refresh.d.ts +6 -0
- package/lib/client-refresh.js +41 -0
- package/lib/client-refresh.js.map +1 -0
- package/lib/client.d.ts +30 -0
- package/lib/client.js +68 -0
- package/lib/client.js.map +7 -0
- package/lib/command.d.ts +18 -0
- package/lib/command.js +118 -0
- package/lib/command.js.map +1 -0
- package/lib/configuration.d.ts +89 -0
- package/lib/configuration.js +31 -0
- package/lib/configuration.js.map +1 -0
- package/lib/content.d.ts +14 -0
- package/lib/content.js +51 -0
- package/lib/content.js.map +1 -0
- package/lib/flavors.d.ts +82 -0
- package/lib/flavors.js +30 -0
- package/lib/flavors.js.map +1 -0
- package/lib/hook-content.d.ts +14 -0
- package/lib/hook-content.js +63 -0
- package/lib/hook-content.js.map +1 -0
- package/lib/index.d.ts +21 -0
- package/lib/index.js +75 -0
- package/lib/index.js.map +1 -0
- package/lib/remote-contract.d.ts +148 -0
- package/lib/remote-contract.js +20 -0
- package/lib/remote-contract.js.map +1 -0
- package/lib/remote.d.ts +20 -0
- package/lib/remote.js +133 -0
- package/lib/remote.js.map +1 -0
- package/lib/review.d.ts +4 -0
- package/lib/review.js +72 -0
- package/lib/review.js.map +1 -0
- package/lib/runtime.d.ts +61 -0
- package/lib/runtime.js +519 -0
- package/lib/runtime.js.map +1 -0
- package/lib/session-compat.d.ts +3 -0
- package/lib/session-compat.js +9 -0
- package/lib/session-compat.js.map +1 -0
- package/lib/settings.d.ts +46 -0
- package/lib/settings.js +44 -0
- package/lib/settings.js.map +1 -0
- package/lib/source.d.ts +10 -0
- package/lib/source.js +32 -0
- package/lib/source.js.map +1 -0
- package/lib/state.d.ts +36 -0
- package/lib/state.js +187 -0
- package/lib/state.js.map +1 -0
- package/lib/terminal-observation.d.ts +4 -0
- package/lib/terminal-observation.js +12 -0
- package/lib/terminal-observation.js.map +1 -0
- package/lib/tool-order.d.ts +9 -0
- package/lib/tool-order.js +82 -0
- package/lib/tool-order.js.map +1 -0
- 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
|