@chrono-meta/fh-gate 1.0.3 → 1.2.0
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/.claude/agents/challenger.md +169 -0
- package/AGENTS.md +160 -0
- package/CATALOG.md +256 -0
- package/CHEATSHEET.md +367 -0
- package/CLAUDE.md +331 -0
- package/CONTRIBUTING.md +198 -0
- package/LICENSE +21 -0
- package/README.md +131 -418
- package/bin/fh-goal.js +9 -0
- package/bin/fh-run.js +9 -0
- package/docs/banner.png +0 -0
- package/docs/codex-compat.md +123 -0
- package/docs/pillars.svg +70 -0
- package/knowledge/shared/harness-core/fh_integration_contract.md +48 -29
- package/package.json +31 -6
- package/plugins/fh-commons/README.md +37 -0
- package/plugins/fh-commons/agents/quench-challenger.md +373 -0
- package/plugins/fh-commons/skills/convergence-loop/SKILL.md +155 -0
- package/plugins/fh-commons/skills/deliberation/SKILL.md +288 -0
- package/plugins/fh-commons/skills/mcp-circuit-breaker/SKILL.md +196 -0
- package/plugins/fh-commons/skills/token-budget-gate/SKILL.md +175 -0
- package/plugins/fh-meta/agents/fact-checker.md +121 -0
- package/plugins/fh-meta/agents/hub-persona-auditor.md +109 -0
- package/plugins/fh-meta/agents/persona-innovator.md +195 -0
- package/plugins/fh-meta/skills/agent-composer/SKILL.md +461 -0
- package/plugins/fh-meta/skills/agent-composer/SKILL_detail.md +464 -0
- package/plugins/fh-meta/skills/apex-review/SKILL.md +185 -0
- package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +135 -0
- package/plugins/fh-meta/skills/contention-layer/SKILL.md +127 -0
- package/plugins/fh-meta/skills/context-bridge-dispatch/SKILL.md +30 -0
- package/plugins/fh-meta/skills/context-bridge-dispatch/SKILL_detail.md +144 -0
- package/plugins/fh-meta/skills/context-doctor/SKILL.md +341 -0
- package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +202 -0
- package/plugins/fh-meta/skills/deep-clarify/SKILL.md +144 -0
- package/plugins/fh-meta/skills/edit-manifest/SKILL.md +210 -0
- package/plugins/fh-meta/skills/field-harvest/SKILL.md +384 -0
- package/plugins/fh-meta/skills/frontier-digest/SKILL.md +272 -0
- package/plugins/fh-meta/skills/goal-quench/SKILL.md +509 -0
- package/plugins/fh-meta/skills/harness-doctor/SKILL.md +277 -0
- package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +484 -0
- package/plugins/fh-meta/skills/harvest-loop/SKILL.md +231 -0
- package/plugins/fh-meta/skills/harvest-loop/SKILL_detail.md +201 -0
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +129 -0
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL_detail.md +158 -0
- package/plugins/fh-meta/skills/install-doctor/SKILL.md +207 -0
- package/plugins/fh-meta/skills/install-wizard/SKILL.md +613 -0
- package/plugins/fh-meta/skills/marketplace-gate/SKILL.md +193 -0
- package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +143 -0
- package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +167 -0
- package/plugins/fh-meta/skills/meta-prompt-builder/SKILL_detail.md +37 -0
- package/plugins/fh-meta/skills/pipeline-conductor/SKILL.md +430 -0
- package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +221 -0
- package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +220 -0
- package/plugins/fh-meta/skills/prompt-regression/SKILL.md +178 -0
- package/plugins/fh-meta/skills/public-surface-audit/SKILL.md +224 -0
- package/plugins/fh-meta/skills/return-path-gate/SKILL.md +257 -0
- package/plugins/fh-meta/skills/self-marketing-lint/SKILL.md +129 -0
- package/plugins/fh-meta/skills/sim-conductor/SKILL.md +364 -0
- package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +337 -0
- package/plugins/fh-meta/skills/skill-splitter/SKILL.md +126 -0
- package/plugins/fh-meta/skills/skill-splitter/SKILL_detail.md +185 -0
- package/plugins/fh-meta/skills/source-grounding-audit/SKILL.md +230 -0
- package/plugins/fh-meta/skills/source-grounding-audit/SKILL_detail.md +182 -0
- package/plugins/fh-meta/skills/steel-quench/SKILL.md +226 -0
- package/plugins/fh-meta/skills/steel-quench/SKILL_detail.md +453 -0
- package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +238 -0
- package/scripts/fh-gate.sh +175 -40
- package/scripts/fh-goal.sh +182 -0
- package/scripts/fh-run.sh +269 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fh-goal.sh — stop-hook-free goal runner for Codex/Claude primary workflows
|
|
3
|
+
#
|
|
4
|
+
# Runs a backend prompt, detects changed files, then runs fh-gate.
|
|
5
|
+
# This is not Claude Code /goal parity; it is the portable quality-gated
|
|
6
|
+
# execution path for environments without Stop hooks.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
VERSION="0.1.0"
|
|
11
|
+
FH_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
_TMPDIR="${TMPDIR:-/tmp}"
|
|
13
|
+
|
|
14
|
+
FH_BACKEND="${FH_BACKEND:-codex}"
|
|
15
|
+
FH_TIMEOUT="${FH_TIMEOUT:-600}"
|
|
16
|
+
FH_GATE_LEVEL="${FH_GATE_LEVEL:-quick}"
|
|
17
|
+
FH_CALLER="${FH_CALLER:-fh-goal}"
|
|
18
|
+
FH_DRY_RUN="${FH_DRY_RUN:-0}"
|
|
19
|
+
FH_VERBOSE="${FH_VERBOSE:-0}"
|
|
20
|
+
GOAL_PROMPT=""
|
|
21
|
+
TARGET_FILES=""
|
|
22
|
+
|
|
23
|
+
usage() {
|
|
24
|
+
cat <<'USAGE'
|
|
25
|
+
Usage:
|
|
26
|
+
fh-goal --prompt <task> [--files "path path"] [--gate quick|full]
|
|
27
|
+
fh-goal "task prompt"
|
|
28
|
+
|
|
29
|
+
Environment:
|
|
30
|
+
FH_BACKEND=codex|claude Backend to run the task (default: codex)
|
|
31
|
+
FH_MODEL=<model> Backend model override
|
|
32
|
+
FH_TIMEOUT=600 Backend timeout seconds
|
|
33
|
+
FH_GATE_LEVEL=quick|full Post-run fh-gate level
|
|
34
|
+
FH_DRY_RUN=1 Print backend prompt and planned gate only
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
FH_BACKEND=codex fh-goal --prompt "Implement X and update tests"
|
|
38
|
+
FH_BACKEND=codex fh-goal --prompt "Review docs" --files "README.md docs/codex-compat.md"
|
|
39
|
+
USAGE
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
while [[ $# -gt 0 ]]; do
|
|
43
|
+
case "$1" in
|
|
44
|
+
--prompt)
|
|
45
|
+
GOAL_PROMPT="${2:-}"
|
|
46
|
+
shift 2
|
|
47
|
+
;;
|
|
48
|
+
--files|--file|--target)
|
|
49
|
+
TARGET_FILES="${2:-}"
|
|
50
|
+
shift 2
|
|
51
|
+
;;
|
|
52
|
+
--gate)
|
|
53
|
+
FH_GATE_LEVEL="${2:-}"
|
|
54
|
+
shift 2
|
|
55
|
+
;;
|
|
56
|
+
--backend)
|
|
57
|
+
FH_BACKEND="${2:-}"
|
|
58
|
+
shift 2
|
|
59
|
+
;;
|
|
60
|
+
--model)
|
|
61
|
+
FH_MODEL="${2:-}"
|
|
62
|
+
shift 2
|
|
63
|
+
;;
|
|
64
|
+
--help|-h)
|
|
65
|
+
usage
|
|
66
|
+
exit 0
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
if [[ -z "$GOAL_PROMPT" ]]; then
|
|
70
|
+
GOAL_PROMPT="$1"
|
|
71
|
+
else
|
|
72
|
+
GOAL_PROMPT="${GOAL_PROMPT} $1"
|
|
73
|
+
fi
|
|
74
|
+
shift
|
|
75
|
+
;;
|
|
76
|
+
esac
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
case "$FH_BACKEND" in
|
|
80
|
+
codex|claude) ;;
|
|
81
|
+
*)
|
|
82
|
+
echo "ERROR: FH_BACKEND must be codex or claude (got: $FH_BACKEND)" >&2
|
|
83
|
+
exit 11
|
|
84
|
+
;;
|
|
85
|
+
esac
|
|
86
|
+
|
|
87
|
+
case "$FH_GATE_LEVEL" in
|
|
88
|
+
quick|full) ;;
|
|
89
|
+
*)
|
|
90
|
+
echo "ERROR: FH_GATE_LEVEL must be quick or full (got: $FH_GATE_LEVEL)" >&2
|
|
91
|
+
exit 11
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
|
|
95
|
+
if [[ -z "$GOAL_PROMPT" ]]; then
|
|
96
|
+
echo "ERROR: missing goal prompt" >&2
|
|
97
|
+
usage >&2
|
|
98
|
+
exit 11
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [[ -z "${FH_MODEL:-}" ]]; then
|
|
102
|
+
case "$FH_BACKEND" in
|
|
103
|
+
codex) FH_MODEL="gpt-5.5" ;;
|
|
104
|
+
claude) FH_MODEL="claude-sonnet-4-6" ;;
|
|
105
|
+
esac
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
if ! command -v "$FH_BACKEND" &>/dev/null; then
|
|
109
|
+
echo "ERROR: '$FH_BACKEND' CLI not found." >&2
|
|
110
|
+
exit 10
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
START_COMMIT="$(git -C "$FH_ROOT" rev-parse HEAD 2>/dev/null || true)"
|
|
114
|
+
PROMPT_FILE=$(mktemp "${_TMPDIR}/fh_goal_prompt_XXXXXX")
|
|
115
|
+
OUTPUT_FILE=$(mktemp "${_TMPDIR}/fh_goal_output_XXXXXX")
|
|
116
|
+
ERR_FILE=$(mktemp "${_TMPDIR}/fh_goal_err_XXXXXX")
|
|
117
|
+
cleanup() { rm -f "$PROMPT_FILE" "$OUTPUT_FILE" "$ERR_FILE"; }
|
|
118
|
+
trap cleanup EXIT
|
|
119
|
+
|
|
120
|
+
cat > "$PROMPT_FILE" <<PROMPT
|
|
121
|
+
FH GOAL RUNNER v${VERSION}
|
|
122
|
+
Backend: ${FH_BACKEND} | Model: ${FH_MODEL}
|
|
123
|
+
|
|
124
|
+
Task:
|
|
125
|
+
${GOAL_PROMPT}
|
|
126
|
+
|
|
127
|
+
Execution rules:
|
|
128
|
+
1. Work in the current repository.
|
|
129
|
+
2. Make the requested changes if implementation is required.
|
|
130
|
+
3. Prefer small, scoped edits.
|
|
131
|
+
4. When finished, summarize changed files and verification performed.
|
|
132
|
+
5. Do not claim FH governance passed; fh-goal runs fh-gate after this backend run.
|
|
133
|
+
PROMPT
|
|
134
|
+
|
|
135
|
+
if [[ "$FH_DRY_RUN" == "1" ]]; then
|
|
136
|
+
cat "$PROMPT_FILE"
|
|
137
|
+
echo
|
|
138
|
+
echo "Planned post-run gate: FH_BACKEND=${FH_BACKEND} scripts/fh-gate.sh \"${TARGET_FILES:-<changed files>}\" ${FH_GATE_LEVEL} ${FH_CALLER}"
|
|
139
|
+
exit 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
echo "→ fh-goal v${VERSION} backend=${FH_BACKEND} model=${FH_MODEL} gate=${FH_GATE_LEVEL}" >&2
|
|
143
|
+
|
|
144
|
+
_TIMEOUT_CMD=""
|
|
145
|
+
if command -v gtimeout &>/dev/null; then
|
|
146
|
+
_TIMEOUT_CMD="gtimeout ${FH_TIMEOUT}"
|
|
147
|
+
elif command -v timeout &>/dev/null; then
|
|
148
|
+
_TIMEOUT_CMD="timeout ${FH_TIMEOUT}"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
run_backend() {
|
|
152
|
+
case "$FH_BACKEND" in
|
|
153
|
+
claude) ${_TIMEOUT_CMD} claude --print --model "$FH_MODEL" ;;
|
|
154
|
+
codex) ${_TIMEOUT_CMD} codex exec -m "$FH_MODEL" - ;;
|
|
155
|
+
esac
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if ! run_backend < "$PROMPT_FILE" > "$OUTPUT_FILE" 2>"$ERR_FILE"; then
|
|
159
|
+
echo "ERROR: ${FH_BACKEND} backend failed or timed out (${FH_TIMEOUT}s)" >&2
|
|
160
|
+
cat "$ERR_FILE" >&2
|
|
161
|
+
exit 10
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
[[ "$FH_VERBOSE" == "1" ]] && cat "$ERR_FILE" >&2
|
|
165
|
+
cat "$OUTPUT_FILE"
|
|
166
|
+
|
|
167
|
+
if [[ -z "$TARGET_FILES" ]]; then
|
|
168
|
+
if [[ -n "$START_COMMIT" ]]; then
|
|
169
|
+
TARGET_FILES=$(git -C "$FH_ROOT" diff "$START_COMMIT"..HEAD --name-only 2>/dev/null | tr '\n' ' ' | xargs || true)
|
|
170
|
+
fi
|
|
171
|
+
if [[ -z "$TARGET_FILES" ]]; then
|
|
172
|
+
TARGET_FILES=$(git -C "$FH_ROOT" status --short 2>/dev/null | awk '{print $2}' | tr '\n' ' ' | xargs || true)
|
|
173
|
+
fi
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
if [[ -z "$TARGET_FILES" ]]; then
|
|
177
|
+
echo "→ fh-goal: no changed files detected; skipping fh-gate" >&2
|
|
178
|
+
exit 0
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
echo "→ fh-goal: running fh-gate on ${TARGET_FILES}" >&2
|
|
182
|
+
FH_BACKEND="$FH_BACKEND" "$FH_ROOT/scripts/fh-gate.sh" "$TARGET_FILES" "$FH_GATE_LEVEL" "$FH_CALLER"
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fh-run.sh — FH runtime adapter for skills and agents
|
|
3
|
+
#
|
|
4
|
+
# Runs a FH skill or agent document through a selectable backend.
|
|
5
|
+
# This is the Codex/Claude bridge for steps that previously assumed
|
|
6
|
+
# Claude Code Agent(...) dispatch or slash-command invocation.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
VERSION="0.1.0"
|
|
11
|
+
FH_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
_TMPDIR="${TMPDIR:-/tmp}"
|
|
13
|
+
|
|
14
|
+
FH_BACKEND="${FH_BACKEND:-auto}"
|
|
15
|
+
FH_TIMEOUT="${FH_TIMEOUT:-180}"
|
|
16
|
+
FH_DRY_RUN="${FH_DRY_RUN:-0}"
|
|
17
|
+
FH_VERBOSE="${FH_VERBOSE:-0}"
|
|
18
|
+
FH_RUN_PROMPT="${FH_RUN_PROMPT:-}"
|
|
19
|
+
FH_RUN_MODE=""
|
|
20
|
+
FH_RUN_NAME=""
|
|
21
|
+
FH_RUN_UNIT=""
|
|
22
|
+
TARGETS=()
|
|
23
|
+
|
|
24
|
+
usage() {
|
|
25
|
+
cat <<'USAGE'
|
|
26
|
+
Usage:
|
|
27
|
+
fh-run --skill <name> [--file <path> ...] [--prompt <text>]
|
|
28
|
+
fh-run --agent <name> [--file <path> ...] [--prompt <text>]
|
|
29
|
+
fh-run --unit <path> [--file <path> ...] [--prompt <text>]
|
|
30
|
+
|
|
31
|
+
Environment:
|
|
32
|
+
FH_BACKEND=auto|codex|claude Runtime backend (default: auto)
|
|
33
|
+
FH_MODEL=<model> Backend model override
|
|
34
|
+
FH_TIMEOUT=180 Backend timeout seconds
|
|
35
|
+
FH_DRY_RUN=1 Print assembled prompt only
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
FH_BACKEND=codex fh-run --skill source-grounding-audit --file docs/foo.md
|
|
39
|
+
FH_BACKEND=codex fh-run --agent fh-commons:quench-challenger --file plugins/fh-meta/skills/foo/SKILL.md
|
|
40
|
+
USAGE
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
while [[ $# -gt 0 ]]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--skill)
|
|
46
|
+
FH_RUN_MODE="skill"
|
|
47
|
+
FH_RUN_NAME="${2:-}"
|
|
48
|
+
shift 2
|
|
49
|
+
;;
|
|
50
|
+
--agent)
|
|
51
|
+
FH_RUN_MODE="agent"
|
|
52
|
+
FH_RUN_NAME="${2:-}"
|
|
53
|
+
shift 2
|
|
54
|
+
;;
|
|
55
|
+
--unit)
|
|
56
|
+
FH_RUN_MODE="unit"
|
|
57
|
+
FH_RUN_UNIT="${2:-}"
|
|
58
|
+
shift 2
|
|
59
|
+
;;
|
|
60
|
+
--file|--target)
|
|
61
|
+
TARGETS+=("${2:-}")
|
|
62
|
+
shift 2
|
|
63
|
+
;;
|
|
64
|
+
--prompt)
|
|
65
|
+
FH_RUN_PROMPT="${2:-}"
|
|
66
|
+
shift 2
|
|
67
|
+
;;
|
|
68
|
+
--backend)
|
|
69
|
+
FH_BACKEND="${2:-}"
|
|
70
|
+
shift 2
|
|
71
|
+
;;
|
|
72
|
+
--model)
|
|
73
|
+
FH_MODEL="${2:-}"
|
|
74
|
+
shift 2
|
|
75
|
+
;;
|
|
76
|
+
--help|-h)
|
|
77
|
+
usage
|
|
78
|
+
exit 0
|
|
79
|
+
;;
|
|
80
|
+
--)
|
|
81
|
+
shift
|
|
82
|
+
while [[ $# -gt 0 ]]; do
|
|
83
|
+
TARGETS+=("$1")
|
|
84
|
+
shift
|
|
85
|
+
done
|
|
86
|
+
;;
|
|
87
|
+
*)
|
|
88
|
+
TARGETS+=("$1")
|
|
89
|
+
shift
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
case "$FH_BACKEND" in
|
|
95
|
+
auto|codex|claude) ;;
|
|
96
|
+
*)
|
|
97
|
+
echo "ERROR: FH_BACKEND must be auto, codex, or claude (got: $FH_BACKEND)" >&2
|
|
98
|
+
exit 11
|
|
99
|
+
;;
|
|
100
|
+
esac
|
|
101
|
+
|
|
102
|
+
if [[ "$FH_BACKEND" == "auto" ]]; then
|
|
103
|
+
if command -v codex &>/dev/null; then
|
|
104
|
+
FH_BACKEND="codex"
|
|
105
|
+
elif command -v claude &>/dev/null; then
|
|
106
|
+
FH_BACKEND="claude"
|
|
107
|
+
else
|
|
108
|
+
echo "ERROR: no supported backend found. Install 'codex' or 'claude'." >&2
|
|
109
|
+
exit 10
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
if [[ -z "${FH_MODEL:-}" ]]; then
|
|
114
|
+
case "$FH_BACKEND" in
|
|
115
|
+
codex) FH_MODEL="gpt-5.5" ;;
|
|
116
|
+
claude) FH_MODEL="claude-sonnet-4-6" ;;
|
|
117
|
+
esac
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
resolve_unit() {
|
|
121
|
+
local mode="$1"
|
|
122
|
+
local name="$2"
|
|
123
|
+
local plugin=""
|
|
124
|
+
local bare="$name"
|
|
125
|
+
|
|
126
|
+
if [[ "$name" == *:* ]]; then
|
|
127
|
+
plugin="${name%%:*}"
|
|
128
|
+
bare="${name#*:}"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
if [[ "$mode" == "unit" ]]; then
|
|
132
|
+
[[ -f "$FH_RUN_UNIT" ]] && printf "%s\n" "$FH_RUN_UNIT" && return 0
|
|
133
|
+
[[ -f "$FH_ROOT/$FH_RUN_UNIT" ]] && printf "%s\n" "$FH_ROOT/$FH_RUN_UNIT" && return 0
|
|
134
|
+
return 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
if [[ "$mode" == "skill" ]]; then
|
|
138
|
+
local candidates=()
|
|
139
|
+
if [[ -n "$plugin" ]]; then
|
|
140
|
+
candidates+=("$FH_ROOT/plugins/$plugin/skills/$bare/SKILL.md")
|
|
141
|
+
fi
|
|
142
|
+
candidates+=(
|
|
143
|
+
"$FH_ROOT/plugins/fh-meta/skills/$bare/SKILL.md"
|
|
144
|
+
"$FH_ROOT/plugins/fh-commons/skills/$bare/SKILL.md"
|
|
145
|
+
)
|
|
146
|
+
for f in "${candidates[@]}"; do
|
|
147
|
+
[[ -f "$f" ]] && printf "%s\n" "$f" && return 0
|
|
148
|
+
done
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
if [[ "$mode" == "agent" ]]; then
|
|
153
|
+
local candidates=()
|
|
154
|
+
if [[ -n "$plugin" ]]; then
|
|
155
|
+
candidates+=("$FH_ROOT/plugins/$plugin/agents/$bare.md")
|
|
156
|
+
fi
|
|
157
|
+
candidates+=(
|
|
158
|
+
"$FH_ROOT/.claude/agents/$bare.md"
|
|
159
|
+
"$FH_ROOT/plugins/fh-meta/agents/$bare.md"
|
|
160
|
+
"$FH_ROOT/plugins/fh-commons/agents/$bare.md"
|
|
161
|
+
)
|
|
162
|
+
for f in "${candidates[@]}"; do
|
|
163
|
+
[[ -f "$f" ]] && printf "%s\n" "$f" && return 0
|
|
164
|
+
done
|
|
165
|
+
return 1
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
return 1
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if [[ -z "$FH_RUN_MODE" ]]; then
|
|
172
|
+
echo "ERROR: choose --skill, --agent, or --unit" >&2
|
|
173
|
+
usage >&2
|
|
174
|
+
exit 11
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
if [[ "$FH_RUN_MODE" != "unit" && -z "$FH_RUN_NAME" ]]; then
|
|
178
|
+
echo "ERROR: missing $FH_RUN_MODE name" >&2
|
|
179
|
+
exit 11
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
UNIT_FILE="$(resolve_unit "$FH_RUN_MODE" "$FH_RUN_NAME" || true)"
|
|
183
|
+
if [[ -z "$UNIT_FILE" ]]; then
|
|
184
|
+
echo "ERROR: unable to resolve FH $FH_RUN_MODE '${FH_RUN_NAME:-$FH_RUN_UNIT}'" >&2
|
|
185
|
+
exit 11
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
PROMPT_FILE=$(mktemp "${_TMPDIR}/fh_run_prompt_XXXXXX")
|
|
189
|
+
OUTPUT_FILE=$(mktemp "${_TMPDIR}/fh_run_output_XXXXXX")
|
|
190
|
+
ERR_FILE=$(mktemp "${_TMPDIR}/fh_run_err_XXXXXX")
|
|
191
|
+
cleanup() { rm -f "$PROMPT_FILE" "$OUTPUT_FILE" "$ERR_FILE"; }
|
|
192
|
+
trap cleanup EXIT
|
|
193
|
+
|
|
194
|
+
{
|
|
195
|
+
printf "FH RUNTIME ADAPTER v%s\n" "$VERSION"
|
|
196
|
+
printf "Backend: %s | Model: %s\n" "$FH_BACKEND" "$FH_MODEL"
|
|
197
|
+
printf "Unit: %s\n" "$UNIT_FILE"
|
|
198
|
+
printf "\n"
|
|
199
|
+
printf "You are executing the FH %s below. Follow its workflow and output contract.\n" "$FH_RUN_MODE"
|
|
200
|
+
printf "If the unit references Claude Code Agent(...) dispatch, treat this invocation as the isolated agent run.\n"
|
|
201
|
+
printf "If the unit references a slash command, execute the documented workflow directly.\n"
|
|
202
|
+
printf "\n"
|
|
203
|
+
if [[ -n "$FH_RUN_PROMPT" ]]; then
|
|
204
|
+
printf "User task:\n%s\n\n" "$FH_RUN_PROMPT"
|
|
205
|
+
fi
|
|
206
|
+
if [[ "${#TARGETS[@]}" -gt 0 ]]; then
|
|
207
|
+
printf "Target paths:\n"
|
|
208
|
+
for t in "${TARGETS[@]}"; do
|
|
209
|
+
printf " - %s\n" "$t"
|
|
210
|
+
done
|
|
211
|
+
printf "\n"
|
|
212
|
+
fi
|
|
213
|
+
printf "===== FH UNIT DOCUMENT START =====\n"
|
|
214
|
+
cat "$UNIT_FILE"
|
|
215
|
+
printf "\n===== FH UNIT DOCUMENT END =====\n"
|
|
216
|
+
|
|
217
|
+
if [[ "${#TARGETS[@]}" -gt 0 ]]; then
|
|
218
|
+
for t in "${TARGETS[@]}"; do
|
|
219
|
+
local_path="$t"
|
|
220
|
+
[[ -f "$local_path" ]] || local_path="$FH_ROOT/$t"
|
|
221
|
+
if [[ -f "$local_path" ]]; then
|
|
222
|
+
printf "\n===== TARGET FILE: %s =====\n" "$t"
|
|
223
|
+
sed -n '1,2000p' "$local_path"
|
|
224
|
+
printf "\n===== END TARGET FILE: %s =====\n" "$t"
|
|
225
|
+
elif [[ -d "$local_path" ]]; then
|
|
226
|
+
printf "\n===== TARGET DIRECTORY: %s =====\n" "$t"
|
|
227
|
+
find "$local_path" -maxdepth 2 -type f | sort | sed "s#^$FH_ROOT/##"
|
|
228
|
+
printf "===== END TARGET DIRECTORY: %s =====\n" "$t"
|
|
229
|
+
else
|
|
230
|
+
printf "\nWARNING: target path not found: %s\n" "$t"
|
|
231
|
+
fi
|
|
232
|
+
done
|
|
233
|
+
fi
|
|
234
|
+
} > "$PROMPT_FILE"
|
|
235
|
+
|
|
236
|
+
if [[ "$FH_DRY_RUN" == "1" ]]; then
|
|
237
|
+
cat "$PROMPT_FILE"
|
|
238
|
+
exit 0
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
if ! command -v "$FH_BACKEND" &>/dev/null; then
|
|
242
|
+
echo "ERROR: '$FH_BACKEND' CLI not found." >&2
|
|
243
|
+
exit 10
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
echo "→ fh-run v${VERSION} backend=${FH_BACKEND} model=${FH_MODEL} unit=${FH_RUN_MODE}:${FH_RUN_NAME:-$FH_RUN_UNIT}" >&2
|
|
247
|
+
|
|
248
|
+
_TIMEOUT_CMD=""
|
|
249
|
+
if command -v gtimeout &>/dev/null; then
|
|
250
|
+
_TIMEOUT_CMD="gtimeout ${FH_TIMEOUT}"
|
|
251
|
+
elif command -v timeout &>/dev/null; then
|
|
252
|
+
_TIMEOUT_CMD="timeout ${FH_TIMEOUT}"
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
run_backend() {
|
|
256
|
+
case "$FH_BACKEND" in
|
|
257
|
+
claude) ${_TIMEOUT_CMD} claude --print --model "$FH_MODEL" ;;
|
|
258
|
+
codex) ${_TIMEOUT_CMD} codex exec -m "$FH_MODEL" - ;;
|
|
259
|
+
esac
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if ! run_backend < "$PROMPT_FILE" > "$OUTPUT_FILE" 2>"$ERR_FILE"; then
|
|
263
|
+
echo "ERROR: ${FH_BACKEND} backend failed or timed out (${FH_TIMEOUT}s)" >&2
|
|
264
|
+
cat "$ERR_FILE" >&2
|
|
265
|
+
exit 10
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
[[ "$FH_VERBOSE" == "1" ]] && cat "$ERR_FILE" >&2
|
|
269
|
+
cat "$OUTPUT_FILE"
|