Run orchestration directly in foreground OpenCode
This commit is contained in:
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch the NestQuest orchestrator immediately in the foreground terminal.
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -gt 1 ]]; then
|
||||
echo "usage: nq-orchestrate [MISSION_PROMPT_FILE|-|--print-prompt]" >&2
|
||||
exit 64
|
||||
fi
|
||||
|
||||
control_home=${NQ_CONTROL_HOME:-/home/overseer/.local/share/nestquest-controller}
|
||||
project=${NQ_PROJECT_ROOT:-/mnt/ai-storage/projects/NestQuest}
|
||||
opencode_bin=${OPENCODE_BIN:-/home/overseer/.opencode/bin/opencode}
|
||||
systemctl_bin=${SYSTEMCTL_BIN:-systemctl}
|
||||
renderer="$control_home/scripts/render-cycle-prompt"
|
||||
profile="$control_home/templates/nestquest.profile.json"
|
||||
|
||||
for required in "$renderer" "$profile"; do
|
||||
[[ -e "$required" ]] || { echo "missing required path: $required" >&2; exit 66; }
|
||||
done
|
||||
|
||||
prompt=$("$renderer" "$profile" --runtime terminal)
|
||||
if [[ ${1:-} == "--print-prompt" ]]; then
|
||||
printf '%s\n' "$prompt"
|
||||
exit 0
|
||||
fi
|
||||
for required in "$project" "$opencode_bin" "$control_home/.opencode/agents/terminal-orchestrator.md"; do
|
||||
[[ -e "$required" ]] || { echo "missing required path: $required" >&2; exit 66; }
|
||||
done
|
||||
if [[ $# -eq 1 ]]; then
|
||||
if [[ "$1" == "-" ]]; then
|
||||
mission=$(cat)
|
||||
else
|
||||
[[ -f "$1" ]] || { echo "mission prompt not found: $1" >&2; exit 66; }
|
||||
mission=$(cat "$1")
|
||||
fi
|
||||
[[ -n "${mission//[[:space:]]/}" ]] || { echo "mission prompt is empty" >&2; exit 65; }
|
||||
prompt+=$'\n\n---\n\n# USER MISSION\n\n'
|
||||
prompt+="$mission"
|
||||
fi
|
||||
|
||||
# Foreground mode owns orchestration. A dormant service must never race it.
|
||||
"$systemctl_bin" --user stop nestquest-controller.service
|
||||
|
||||
session_status=$("$control_home/scripts/nq-controller" --check-interactive)
|
||||
if [[ "$session_status" == "interactive NestQuest session open" ]]; then
|
||||
echo "Another interactive NestQuest OpenCode session is open." >&2
|
||||
echo "Close it, then run nq-orchestrate again." >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
export OPENCODE_CONFIG_DIR="$control_home/.opencode"
|
||||
exec "$opencode_bin" "$project" --pure --agent terminal-orchestrator --prompt "$prompt"
|
||||
@@ -11,6 +11,7 @@ import sys
|
||||
|
||||
|
||||
TEMPLATE = Path(__file__).resolve().parent.parent / "templates/ORCHESTRATOR-PROMPT.template.md"
|
||||
RUNTIME_DIR = TEMPLATE.parent / "runtime"
|
||||
PLACEHOLDER = re.compile(r"\{\{([A-Z][A-Z0-9_]*)\}\}")
|
||||
|
||||
|
||||
@@ -28,15 +29,27 @@ def render(template: str, profile: dict[str, str]) -> str:
|
||||
return result
|
||||
|
||||
|
||||
def select_runtime(template: str, runtime: str) -> str:
|
||||
if runtime == "service":
|
||||
return template
|
||||
marker = "# 27. HOST CONTROLLER RUNTIME"
|
||||
if template.count(marker) != 1:
|
||||
raise ValueError("controller runtime section is missing or duplicated")
|
||||
prefix = template.split(marker, 1)[0].rstrip()
|
||||
overlay = (RUNTIME_DIR / f"{runtime}.md").read_text().strip()
|
||||
return prefix + "\n\n" + overlay + "\n"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("profile", type=Path)
|
||||
parser.add_argument("--runtime", choices=("service", "terminal"), default="service")
|
||||
parser.add_argument("--output", type=Path)
|
||||
args = parser.parse_args()
|
||||
profile = json.loads(args.profile.read_text())
|
||||
if not isinstance(profile, dict):
|
||||
parser.error("profile must be a JSON object")
|
||||
content = render(TEMPLATE.read_text(), profile)
|
||||
content = render(select_runtime(TEMPLATE.read_text(), args.runtime), profile)
|
||||
if args.output:
|
||||
args.output.write_text(content)
|
||||
print(args.output)
|
||||
|
||||
Reference in New Issue
Block a user