mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
Remove orchestration skill setting
This commit is contained in:
@@ -53,6 +53,12 @@ Native builds for each platform on [**Releases**](https://github.com/l0ng-ai/tty
|
|||||||
Terminal and keybinding reference: [docs/features.md](docs/features.md). The agent-facing CLI
|
Terminal and keybinding reference: [docs/features.md](docs/features.md). The agent-facing CLI
|
||||||
interface is documented in [skills/tty7/SKILL.md](skills/tty7/SKILL.md).
|
interface is documented in [skills/tty7/SKILL.md](skills/tty7/SKILL.md).
|
||||||
|
|
||||||
|
Install the skill with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx skills add l0ng-ai/tty7
|
||||||
|
```
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1,
|
Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1,
|
||||||
|
|||||||
@@ -53,6 +53,12 @@
|
|||||||
终端和快捷键参考:[docs/features.zh-CN.md](docs/features.zh-CN.md)。面向 agent 的 CLI 接口见
|
终端和快捷键参考:[docs/features.zh-CN.md](docs/features.zh-CN.md)。面向 agent 的 CLI 接口见
|
||||||
[skills/tty7/SKILL.md](skills/tty7/SKILL.md)。
|
[skills/tty7/SKILL.md](skills/tty7/SKILL.md)。
|
||||||
|
|
||||||
|
通过以下命令安装 skill:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx skills add l0ng-ai/tty7
|
||||||
|
```
|
||||||
|
|
||||||
## 基准测试
|
## 基准测试
|
||||||
|
|
||||||
同一台机器、同一天、统一 155×40 网格 —— Apple M1 Pro,macOS 26.3.1,
|
同一台机器、同一天、统一 155×40 网格 —— Apple M1 Pro,macOS 26.3.1,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: tty7
|
name: tty7
|
||||||
description: Drive the tty7 terminal workbench from the shell with the `tty7` binary — list workspaces/tabs/panes, split a pane, send keystrokes into one, capture what is on a pane's screen, run a command in a real PTY and pass its exit code through, see which coding agents are running and which ports a pane is listening on. Use this whenever tty7, panes, workspaces, or `%42`/`@7`/"the other pane"/"the other agent" come up; whenever you need to start something long-running or interactive (dev server, REPL, ssh session, `tail -f`, a TUI) that should not sit blocking your Bash tool; whenever a program needs a real terminal to behave the way the user sees it; and whenever you need to look at or report on what is running in some *other* terminal on this machine. Cheap to check: if `$TTY7_PANE` is set you are already inside tty7 and every command here works with no setup.
|
description: >-
|
||||||
|
Drive the tty7 terminal workbench from the shell with the `tty7` binary — list workspaces/tabs/panes, split a pane, send keystrokes into one, capture what is on a pane's screen, run a command in a real PTY and pass its exit code through, see which coding agents are running and which ports a pane is listening on. Use this whenever tty7, panes, workspaces, or `%42`/`@7`/"the other pane"/"the other agent" come up; whenever you need to start something long-running or interactive (dev server, REPL, ssh session, `tail -f`, a TUI) that should not sit blocking your Bash tool; whenever a program needs a real terminal to behave the way the user sees it; and whenever you need to look at or report on what is running in some *other* terminal on this machine. Cheap to check: if `$TTY7_PANE` is set you are already inside tty7 and every command here works with no setup.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Driving tty7 from the command line
|
# Driving tty7 from the command line
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ pub mod agent_prompt;
|
|||||||
pub mod cli_install;
|
pub mod cli_install;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod keychain;
|
pub mod keychain;
|
||||||
pub mod orchestration_skill;
|
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod ssh_config;
|
pub mod ssh_config;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
//! The tty7 orchestration *skill*: a Claude Code skill file describing how a
|
|
||||||
//! primary agent delegates work to worker panes over the session CLI
|
|
||||||
//! (`tab new` / `send` / `wait` / `capture`), installed at
|
|
||||||
//! `~/.claude/skills/tty7-orchestration/SKILL.md`.
|
|
||||||
//!
|
|
||||||
//! A skill, deliberately not a global instruction. An earlier cut of this
|
|
||||||
//! feature appended guidance to `~/.claude/CLAUDE.md` / `~/.codex/AGENTS.md`,
|
|
||||||
//! which taxed every session's context window and — worse — encouraged *every*
|
|
||||||
//! agent to discover and orchestrate its neighbours. The common shape is
|
|
||||||
//! primary → workers: one agent owns decomposition, dispatch, waiting and
|
|
||||||
//! aggregation, and the workers just do bounded tasks. A skill fits that
|
|
||||||
//! exactly: only its one-line description rides in context until the user or
|
|
||||||
//! the primary agent explicitly reaches for it, and workers never see it.
|
|
||||||
//!
|
|
||||||
//! The file is wholly tty7-owned (marker inside, checked before any delete),
|
|
||||||
//! so install is a plain overwrite — also the version-refresh path — and
|
|
||||||
//! uninstall removes the file, never guessing at merged user edits.
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
/// Ownership marker. Uninstall refuses to delete a file without it, so a
|
|
||||||
/// hand-written skill that happens to share the directory name survives.
|
|
||||||
const MARKER: &str = "<!-- managed by tty7 (Settings → Agents); edits are overwritten -->";
|
|
||||||
|
|
||||||
const SKILL_DIR: &str = "tty7-orchestration";
|
|
||||||
|
|
||||||
/// The skill itself. The frontmatter description is what Claude Code matches
|
|
||||||
/// against a session's intent, so it names the *tasks* that should trigger it;
|
|
||||||
/// the body can afford real workflow detail because it only loads on use.
|
|
||||||
const SKILL: &str = "\
|
|
||||||
---
|
|
||||||
name: tty7-orchestration
|
|
||||||
description: Delegate work to other coding agents running in tty7 terminal panes — spawn a worker pane, send it a prompt, wait until it needs input or finishes, and capture its output. Use when asked to parallelize work across agents, run an agent team, or drive another terminal session in tty7.
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- managed by tty7 (Settings → Agents); edits are overwritten -->
|
|
||||||
|
|
||||||
# Orchestrating tty7 sessions
|
|
||||||
|
|
||||||
You are the primary agent; panes you create are workers. Keep workers
|
|
||||||
bounded: give each a self-contained task, and keep decomposition, waiting,
|
|
||||||
and aggregation here. Do not hand workers orchestration duties — a worker
|
|
||||||
that finishes its task and stops is what keeps an agent team debuggable.
|
|
||||||
|
|
||||||
Prerequisites: you are inside tty7 (the `TTY7` env var is set) and the
|
|
||||||
`tty7` CLI is on PATH. `%N` addresses a pane by id; `$TTY7_PANE` is your own
|
|
||||||
pane. Every verb takes `--json`.
|
|
||||||
|
|
||||||
## The delegation loop
|
|
||||||
|
|
||||||
1. Create a worker pane: `tty7 tab new --cwd DIR` — prints the pane id (`%N`)
|
|
||||||
2. Start the worker: `tty7 send %N 'claude \"one bounded task\"' --enter`
|
|
||||||
- interactive, not `claude -p`: headless print mode never stops to ask,
|
|
||||||
so the `waiting` state this loop turns on would never arrive
|
|
||||||
3. Sleep until it needs you:
|
|
||||||
`tty7 wait %N --until waiting,done --changed --timeout 600`
|
|
||||||
- exit 0: the JSON report names the matched state, with the agent's
|
|
||||||
message and native session id
|
|
||||||
- exit 124: still working — wait again, or look in on it
|
|
||||||
- exit 1 with `\"status\": \"exit\"`: the worker died; do not wait again
|
|
||||||
4. If it is *waiting* (a permission prompt or question), read and answer it:
|
|
||||||
`tty7 capture %N --plain`, then `tty7 send %N 'y' --enter` (or whatever
|
|
||||||
the prompt asks) — then go back to step 3
|
|
||||||
5. When *done*, collect the result: `tty7 capture %N --plain`
|
|
||||||
6. Clean up: `tty7 pane close %N`
|
|
||||||
|
|
||||||
Always pass `--changed` when you wait after sending something. The status the
|
|
||||||
server keeps is a level, not an event: `done` stands until the next turn
|
|
||||||
begins and `waiting` stands until the agent moves, so a plain `wait` issued
|
|
||||||
right after a `send` answers with the *previous* turn's state before the
|
|
||||||
worker has even read your input. `--changed` ignores the state the pane was
|
|
||||||
already in. Without it, check `\"stale\": true` in the JSON before trusting a
|
|
||||||
wake-up.
|
|
||||||
|
|
||||||
Run workers in parallel by repeating steps 1–2, then waiting on each pane.
|
|
||||||
`tty7 ls` shows every workspace, tab and pane; `tty7 agents` shows every
|
|
||||||
agent and its status at a glance.
|
|
||||||
";
|
|
||||||
|
|
||||||
/// The skill's install path: `~/.claude/skills/tty7-orchestration/SKILL.md`,
|
|
||||||
/// honoring the same `CLAUDE_CONFIG_DIR` override the hooks installer honors.
|
|
||||||
fn skill_path() -> Option<PathBuf> {
|
|
||||||
let base = if let Some(dir) = std::env::var_os("CLAUDE_CONFIG_DIR").filter(|d| !d.is_empty()) {
|
|
||||||
PathBuf::from(dir)
|
|
||||||
} else {
|
|
||||||
home_dir()?.join(".claude")
|
|
||||||
};
|
|
||||||
Some(base.join("skills").join(SKILL_DIR).join("SKILL.md"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn home_dir() -> Option<PathBuf> {
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
std::env::var_os("HOME").map(PathBuf::from)
|
|
||||||
}
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
{
|
|
||||||
std::env::var_os("USERPROFILE").map(PathBuf::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install (or refresh) the skill. A plain overwrite: the file is wholly
|
|
||||||
/// tty7-owned, and this doubling as the version-refresh path is the point.
|
|
||||||
pub fn install() -> anyhow::Result<String> {
|
|
||||||
let path = skill_path().ok_or_else(|| anyhow::anyhow!("cannot resolve home directory"))?;
|
|
||||||
if let Some(parent) = path.parent() {
|
|
||||||
std::fs::create_dir_all(parent)?;
|
|
||||||
}
|
|
||||||
crate::core::config::write_atomic(&path, SKILL.as_bytes())?;
|
|
||||||
Ok("Installed".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the skill — but only a file carrying the ownership marker, so a
|
|
||||||
/// user's own `tty7-orchestration` skill is never deleted by tty7. The
|
|
||||||
/// directory goes too once empty; an empty skill dir would read as a broken
|
|
||||||
/// skill in Claude Code's listing.
|
|
||||||
pub fn uninstall() -> anyhow::Result<String> {
|
|
||||||
let path = skill_path().ok_or_else(|| anyhow::anyhow!("cannot resolve home directory"))?;
|
|
||||||
match std::fs::read_to_string(&path) {
|
|
||||||
Ok(content) if content.contains(MARKER) => {
|
|
||||||
std::fs::remove_file(&path)?;
|
|
||||||
if let Some(dir) = path.parent() {
|
|
||||||
let _ = std::fs::remove_dir(dir); // fails non-empty; that's the guard
|
|
||||||
}
|
|
||||||
Ok("Removed".to_string())
|
|
||||||
}
|
|
||||||
Ok(_) => anyhow::bail!(
|
|
||||||
"{} exists but was not installed by tty7 — not touching it",
|
|
||||||
path.display()
|
|
||||||
),
|
|
||||||
Err(_) => Ok("Removed".to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the tty7-owned skill is currently installed.
|
|
||||||
pub fn installed() -> bool {
|
|
||||||
skill_path().is_some_and(|p| {
|
|
||||||
std::fs::read_to_string(p)
|
|
||||||
.map(|s| s.contains(MARKER))
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// The skill text itself is load-bearing: the frontmatter must parse as a
|
|
||||||
/// skill (name + description) and the body must carry the ownership
|
|
||||||
/// marker `uninstall` keys on.
|
|
||||||
#[test]
|
|
||||||
fn skill_content_is_well_formed() {
|
|
||||||
assert!(SKILL.starts_with("---\nname: tty7-orchestration\n"));
|
|
||||||
assert!(SKILL.contains("\ndescription: "));
|
|
||||||
// Frontmatter is closed before the body starts.
|
|
||||||
assert_eq!(SKILL.matches("\n---\n").count(), 1);
|
|
||||||
assert!(SKILL.contains(MARKER));
|
|
||||||
// The loop teaches the four primitives, not a stale verb set.
|
|
||||||
for verb in ["tab new", "send %N", "wait %N", "capture %N", "pane close"] {
|
|
||||||
assert!(SKILL.contains(verb), "skill body lost `{verb}`");
|
|
||||||
}
|
|
||||||
// The whole loop rests on `--changed`: without it a wait issued right
|
|
||||||
// after a send answers with the previous turn's status.
|
|
||||||
assert!(SKILL.contains("--changed"), "the loop lost --changed");
|
|
||||||
// And the worker must be *launched* interactively — `claude -p` never
|
|
||||||
// reaches the `waiting` state steps 3–4 are built on. The prose may
|
|
||||||
// still name it; the command in step 2 may not start with it.
|
|
||||||
assert!(
|
|
||||||
!SKILL.contains("'claude -p"),
|
|
||||||
"headless print mode cannot produce the `waiting` state this loop waits for"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Owns `CLAUDE_CONFIG_DIR` and a scratch directory for the length of a
|
|
||||||
/// test, and puts both back on the way out — including on a panic, which
|
|
||||||
/// a plain tail cleanup would skip, leaving the var set for whatever runs
|
|
||||||
/// next in this process.
|
|
||||||
struct ScratchConfigDir(PathBuf);
|
|
||||||
|
|
||||||
impl ScratchConfigDir {
|
|
||||||
fn new(tag: &str) -> ScratchConfigDir {
|
|
||||||
let dir =
|
|
||||||
std::env::temp_dir().join(format!("tty7-skill-test-{}-{tag}", std::process::id()));
|
|
||||||
let _ = std::fs::remove_dir_all(&dir);
|
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
// SAFETY: test-scoped env mutation. `skill_path` is the only
|
|
||||||
// reader of this var in this binary, and the guard restores it.
|
|
||||||
unsafe { std::env::set_var("CLAUDE_CONFIG_DIR", &dir) };
|
|
||||||
ScratchConfigDir(dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ScratchConfigDir {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// SAFETY: as above — undoing what `new` did.
|
|
||||||
unsafe { std::env::remove_var("CLAUDE_CONFIG_DIR") };
|
|
||||||
let _ = std::fs::remove_dir_all(&self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install → installed → uninstall round-trips against a scratch
|
|
||||||
/// `CLAUDE_CONFIG_DIR`; a foreign (marker-less) file is refused, not
|
|
||||||
/// deleted. Env-var scoped: this test owns the var for its duration.
|
|
||||||
#[test]
|
|
||||||
fn install_roundtrip_and_foreign_file_safety() {
|
|
||||||
let guard = ScratchConfigDir::new("roundtrip");
|
|
||||||
let scratch = guard.0.clone();
|
|
||||||
|
|
||||||
assert!(!installed());
|
|
||||||
install().unwrap();
|
|
||||||
assert!(installed());
|
|
||||||
let path = scratch.join("skills").join(SKILL_DIR).join("SKILL.md");
|
|
||||||
assert_eq!(std::fs::read_to_string(&path).unwrap(), SKILL);
|
|
||||||
|
|
||||||
// Re-install is the refresh path: same content, no error.
|
|
||||||
install().unwrap();
|
|
||||||
assert!(installed());
|
|
||||||
|
|
||||||
uninstall().unwrap();
|
|
||||||
assert!(!installed());
|
|
||||||
assert!(!path.exists());
|
|
||||||
assert!(!path.parent().unwrap().exists(), "empty skill dir lingers");
|
|
||||||
|
|
||||||
// A user's own skill under our name must survive an uninstall.
|
|
||||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
|
||||||
std::fs::write(&path, "---\nname: tty7-orchestration\n---\nmy own\n").unwrap();
|
|
||||||
assert!(!installed(), "a foreign file is not a tty7 install");
|
|
||||||
assert!(uninstall().is_err());
|
|
||||||
assert!(path.exists(), "the user's file was deleted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3518,8 +3518,6 @@ impl Tty7App {
|
|||||||
agent_hooks_states: crate::ui::settings::AgentHooksView::Loading,
|
agent_hooks_states: crate::ui::settings::AgentHooksView::Loading,
|
||||||
agent_hooks_seq: 0,
|
agent_hooks_seq: 0,
|
||||||
agent_hooks_note: None,
|
agent_hooks_note: None,
|
||||||
orchestration_skill: None,
|
|
||||||
orchestration_skill_note: None,
|
|
||||||
_subs: subs,
|
_subs: subs,
|
||||||
});
|
});
|
||||||
let search_focus = self
|
let search_focus = self
|
||||||
@@ -4170,7 +4168,6 @@ impl Tty7App {
|
|||||||
s.recording = None;
|
s.recording = None;
|
||||||
if target == SettingsSection::Agents {
|
if target == SettingsSection::Agents {
|
||||||
s.agent_hooks_states = crate::ui::settings::AgentHooksView::Loading;
|
s.agent_hooks_states = crate::ui::settings::AgentHooksView::Loading;
|
||||||
s.orchestration_skill_note = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.ensure_agent_hooks_loaded(cx);
|
self.ensure_agent_hooks_loaded(cx);
|
||||||
@@ -4183,7 +4180,6 @@ impl Tty7App {
|
|||||||
.is_some_and(|s| s.section == SettingsSection::Agents)
|
.is_some_and(|s| s.section == SettingsSection::Agents)
|
||||||
{
|
{
|
||||||
self.load_agent_hooks_states(cx);
|
self.load_agent_hooks_states(cx);
|
||||||
self.load_orchestration_skill_state(cx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4321,60 +4317,6 @@ impl Tty7App {
|
|||||||
Some((host, Some(home)))
|
Some((host, Some(home)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Settings → Agents: the "Orchestration skill" switch — install or
|
|
||||||
/// remove the Claude Code skill that teaches a primary agent the
|
|
||||||
/// delegation workflow over the session CLI.
|
|
||||||
///
|
|
||||||
/// Off the UI thread and with the outcome surfaced, like the hook rows:
|
|
||||||
/// the one error a user actually hits — uninstall refusing a file tty7
|
|
||||||
/// did not write — is exactly the case where a switch that silently
|
|
||||||
/// springs back explains nothing.
|
|
||||||
pub(crate) fn set_orchestration_skill(&mut self, on: bool, cx: &mut Context<Self>) {
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
let result = cx
|
|
||||||
.background_spawn(async move {
|
|
||||||
if on {
|
|
||||||
crate::core::orchestration_skill::install()
|
|
||||||
} else {
|
|
||||||
crate::core::orchestration_skill::uninstall()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
let note = match result {
|
|
||||||
Ok(summary) => summary,
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("orchestration-skill change failed: {e}");
|
|
||||||
format!("Failed: {e}")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let _ = this.update(cx, |this, cx| {
|
|
||||||
if let Some(s) = this.settings.as_mut() {
|
|
||||||
s.orchestration_skill_note = Some(note);
|
|
||||||
}
|
|
||||||
this.load_orchestration_skill_state(cx);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Re-read the skill's presence from disk into `SettingsState`. The file
|
|
||||||
/// is the truth — an edit made outside this panel shows up here — but it
|
|
||||||
/// is read on open and after a change, never once per rendered frame.
|
|
||||||
fn load_orchestration_skill_state(&mut self, cx: &mut Context<Self>) {
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
let installed = cx
|
|
||||||
.background_spawn(async { crate::core::orchestration_skill::installed() })
|
|
||||||
.await;
|
|
||||||
let _ = this.update(cx, |this, cx| {
|
|
||||||
if let Some(s) = this.settings.as_mut() {
|
|
||||||
s.orchestration_skill = Some(installed);
|
|
||||||
}
|
|
||||||
cx.notify();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn settings_install_agent_hooks(
|
pub(crate) fn settings_install_agent_hooks(
|
||||||
&mut self,
|
&mut self,
|
||||||
agent: crate::core::agent_hooks::HookAgent,
|
agent: crate::core::agent_hooks::HookAgent,
|
||||||
|
|||||||
@@ -434,10 +434,6 @@ pub(crate) struct SettingsState {
|
|||||||
pub(crate) agent_hooks_states: AgentHooksView,
|
pub(crate) agent_hooks_states: AgentHooksView,
|
||||||
pub(crate) agent_hooks_seq: u64,
|
pub(crate) agent_hooks_seq: u64,
|
||||||
pub(crate) agent_hooks_note: Option<(crate::core::agent_hooks::HookAgent, String)>,
|
pub(crate) agent_hooks_note: Option<(crate::core::agent_hooks::HookAgent, String)>,
|
||||||
/// Whether the orchestration skill is on disk. `None` until the first
|
|
||||||
/// read lands — cached rather than probed per frame, like the hook rows.
|
|
||||||
pub(crate) orchestration_skill: Option<bool>,
|
|
||||||
pub(crate) orchestration_skill_note: Option<String>,
|
|
||||||
pub(crate) _subs: Vec<Subscription>,
|
pub(crate) _subs: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3563,14 +3559,6 @@ impl Tty7App {
|
|||||||
),
|
),
|
||||||
None => (AgentHooksView::Loading, None, HostId::LOCAL),
|
None => (AgentHooksView::Loading, None, HostId::LOCAL),
|
||||||
};
|
};
|
||||||
let (skill_on, skill_note) = match self.active_settings() {
|
|
||||||
Some(s) => (
|
|
||||||
s.orchestration_skill.unwrap_or(false),
|
|
||||||
s.orchestration_skill_note.clone(),
|
|
||||||
),
|
|
||||||
None => (false, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut page = v_flex().child(self.section_intro(
|
let mut page = v_flex().child(self.section_intro(
|
||||||
"Agents",
|
"Agents",
|
||||||
"Hook integrations give panes running these agents live session status \
|
"Hook integrations give panes running these agents live session status \
|
||||||
@@ -3578,44 +3566,6 @@ impl Tty7App {
|
|||||||
cx,
|
cx,
|
||||||
));
|
));
|
||||||
|
|
||||||
// The orchestration skill (see `core::orchestration_skill`): a Claude
|
|
||||||
// Code skill a primary agent invokes explicitly to delegate work to
|
|
||||||
// worker panes over the session CLI. The filesystem stays the truth —
|
|
||||||
// an edit made outside this panel shows up here — but it is read into
|
|
||||||
// `SettingsState` when the page opens, not probed once per frame.
|
|
||||||
// Above the machine picker: the skill lives on this machine's disk
|
|
||||||
// regardless of which host's hooks are managed below.
|
|
||||||
let skill_switch =
|
|
||||||
v_flex()
|
|
||||||
.items_end()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
crate::ui::theme::switch("orchestration-skill", cx)
|
|
||||||
.checked(skill_on)
|
|
||||||
.on_click(cx.listener(|this, on: &bool, _w, cx| {
|
|
||||||
this.set_orchestration_skill(*on, cx)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.when_some(skill_note, |col, text| {
|
|
||||||
col.child(
|
|
||||||
div()
|
|
||||||
.max_w_80()
|
|
||||||
.text_xs()
|
|
||||||
.text_right()
|
|
||||||
.text_color(muted_fg)
|
|
||||||
.child(text),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.into_any_element();
|
|
||||||
page = page.child(self.settings_row(
|
|
||||||
"Orchestration skill",
|
|
||||||
"Install a Claude Code skill (~/.claude/skills/tty7-orchestration) that teaches a \
|
|
||||||
primary agent to delegate work to worker panes over the `tty7` CLI — spawn, send, \
|
|
||||||
wait, capture — invoked explicitly, never injected globally",
|
|
||||||
skill_switch,
|
|
||||||
cx,
|
|
||||||
));
|
|
||||||
|
|
||||||
page = page.children(self.agent_hooks_machine_picker(selected_host, cx));
|
page = page.children(self.agent_hooks_machine_picker(selected_host, cx));
|
||||||
|
|
||||||
match view {
|
match view {
|
||||||
|
|||||||
Reference in New Issue
Block a user