feat(agent): deprecate and completely remove cline agent support

- Delete adapters/cline.py and unregister from registry.py
- Remove cline branches from lib.sh and all 8 skill scripts (create, resume, stop, status, reconcile, update_yaml_resumed, resolve_session_id, orc_onboard)
- Narrow own-key mapping dictionaries across lib_py core modules to 4 supported agents
- Delete cline-exclusive tests and retarget shared fixtures to grok/hermes/claude
- Update skills documentation and installation guides (439 passed, 0 failures)
- Archive cline deprecation consensus and review reports
This commit is contained in:
2026-08-28 22:38:48 +09:00
parent e0c0c107f5
commit f57cd5cdde
40 changed files with 530 additions and 411 deletions
+13 -41
View File
@@ -30,7 +30,6 @@ def test_agent_adapter_registry():
'claude': 'claude_session_id_own',
'agy': 'agy_conversation_id_own',
'hermes': 'hermes_conversation_id_own',
'cline': 'cline_conversation_id_own',
'grok': 'grok_session_id_own',
}
for agent, expected in EXPECTED_OWN_KEYS.items():
@@ -50,8 +49,8 @@ def test_agent_of_row_priority():
assert agent_of_row(row2) == 'hermes'
# Priority 3: pane.cmd exact match
row3 = {'pane': {'cmd': 'cline'}}
assert agent_of_row(row3) == 'cline'
row3 = {'pane': {'cmd': 'grok'}}
assert agent_of_row(row3) == 'grok'
def test_agent_of_row_pane_cmd_binary_path_and_failure():
# pane.cmd 가 절대 경로 형태여도 해석된다
@@ -73,7 +72,6 @@ def test_adapter_required_properties():
'claude': ('Anthropic|Assistant|Chat|Welcome|Claude Code|Opus|Sonnet|Haiku', '/exit', 'claude-code', ('session_id', 'session_jsonl', 'session_size_bytes', 'session_lines')),
'agy': ('Antigravity', 'Exit', 'antigravity-cli', ('conversation_id', 'conversation_db', 'conversation_brain_dir')),
'hermes': ('Hermes|Welcome to Hermes Agent|NOUS HERMES', '/exit', 'hermes-agent', ('session_id',)),
'cline': ('Cline|history|Chat|What can I do|slash commands', '/exit', 'cline-agent', ('session_id',)),
'grok': ('Grok|xAI|Assistant||>>>', '/exit', 'grok-build', ('session_id', 'session_jsonl')),
}
for agent, (toks, exitk, delk, cache_f) in expected.items():
@@ -83,13 +81,15 @@ def test_adapter_required_properties():
assert adapter.exit_key == exitk
assert adapter.delegate_agent_key == delk
assert adapter.identity_cache_fields == cache_f
# 2-Tier readiness default contract
assert adapter.strong_ready_tokens == adapter.ready_tokens
assert adapter.weak_ready_tokens == ''
def test_adapter_modal_tokens_contract():
"""F-8: Verify adapter-level modal_tokens contract (T-2d)."""
expected_modal = {
'claude': 'Try the new fullscreen renderer\\?',
'cline': 'Select API Provider|Enter API Key|Select an API provider|API Provider|Cline API key',
'agy': None,
'hermes': None,
'grok': None,
@@ -106,7 +106,7 @@ def test_facts_bridge_eval_contract():
env = os.environ.copy()
skills_dir = str(Path(__file__).resolve().parent.parent / ".agents" / "skills")
env["PYTHONPATH"] = f"{skills_dir}:{env.get('PYTHONPATH', '')}"
for agent in ('claude', 'agy', 'hermes', 'cline', 'grok'):
for agent in ('claude', 'agy', 'hermes', 'grok'):
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "facts", agent], capture_output=True, text=True, env=env)
assert res.returncode == 0
facts_output = res.stdout
@@ -115,12 +115,12 @@ def test_facts_bridge_eval_contract():
bash_cmd = f"""
set -euo pipefail
eval {shlex_quote(facts_output)}
echo "AGENT=$MAM_AGENT_NAME|OWN=$MAM_OWN_KEY|TOK=$MAM_READY_TOKENS|EXIT=$MAM_EXIT_KEY|DEL=$MAM_DELEGATE_AGENT_KEY|PH=$MAM_INPUT_PLACEHOLDER"
echo "AGENT=$MAM_AGENT_NAME|OWN=$MAM_OWN_KEY|TOK=$MAM_READY_TOKENS|EXIT=$MAM_EXIT_KEY|DEL=$MAM_DELEGATE_AGENT_KEY|PH=$MAM_INPUT_PLACEHOLDER|STRONG=$MAM_STRONG_READY_TOKENS|WEAK=$MAM_WEAK_READY_TOKENS"
"""
res_bash = subprocess.run(["bash", "-c", bash_cmd], capture_output=True, text=True)
assert res_bash.returncode == 0, f"Bash eval failed for {agent}:\nStdout: {res_bash.stdout}\nStderr: {res_bash.stderr}"
if agent == 'cline':
assert "PH=Ask anything..." in res_bash.stdout
assert f"STRONG={get_adapter(agent).strong_ready_tokens}" in res_bash.stdout
assert "WEAK=" in res_bash.stdout
def shlex_quote(s):
import shlex
@@ -187,18 +187,7 @@ def test_purge_artifacts_composite(tmp_path):
assert conn.execute("SELECT count(*) FROM messages WHERE session_id='uuid-h'").fetchone()[0] == 0
conn.close()
# 4. Cline (sessions dir)
cline_adapter = get_adapter('cline')
cline_ctx = DiscoveryContext(workspace=ws, agent_name='cline', home_dir=home)
cline_dir = f"{home}/.cline/data/sessions/uuid-cl"
os.makedirs(cline_dir, exist_ok=True)
with open(f"{cline_dir}/uuid-cl.json", 'w') as f:
f.write('{"session_id": "uuid-cl"}')
purged_cl = cline_adapter.purge_artifacts('uuid-cl', cline_ctx)
assert len(purged_cl) == 1
assert not os.path.exists(cline_dir)
# 5. Grok (session directory containing chat_history.jsonl)
# 4. Grok (session directory containing chat_history.jsonl)
grok_adapter = get_adapter('grok')
grok_ctx = DiscoveryContext(workspace=ws, agent_name='grok', home_dir=home)
g_path = grok_adapter.artifact_path('uuid-g', grok_ctx)
@@ -227,11 +216,6 @@ def test_adapter_spawn_and_resume_specs():
assert hermes.resume_spec('hermes', 'u1', materialized=True) == 'hermes --resume u1 --no-restore-cwd --yolo --accept-hooks'
assert hermes.resume_spec('hermes', 'u1', materialized=False) == 'hermes --yolo --accept-hooks'
cline = get_adapter('cline')
assert cline.spawn_spec('cline', 'u1') == 'cline -i'
assert cline.resume_spec('cline', 'u1', materialized=True) == 'cline -i --id u1'
assert cline.resume_spec('cline', 'u1', materialized=False) == 'cline -i'
grok = get_adapter('grok')
assert grok.spawn_spec('grok', 'u1') == 'grok --session-id u1 --permission-mode bypassPermissions'
assert grok.spawn_spec('grok', '') == 'grok --permission-mode bypassPermissions'
@@ -264,9 +248,8 @@ def test_adapter_auth_ok(tmp_path, monkeypatch):
grok_auth.write_text("{}")
assert grok.auth_ok() is True
# Hermes & Cline always True
# Hermes always True
assert get_adapter('hermes').auth_ok() is True
assert get_adapter('cline').auth_ok() is True
def test_adapter_discover(tmp_path):
import sqlite3
@@ -314,16 +297,7 @@ def test_adapter_discover(tmp_path):
conn.close()
assert hermes.discover(ctx_h) == ['u-h1']
# 4. Cline
cline = get_adapter('cline')
ctx_cl = DiscoveryContext(workspace=ws, agent_name='cline', home_dir=home)
cl_sess = f"{home}/.cline/data/sessions/u-cl1"
os.makedirs(cl_sess, exist_ok=True)
with open(f"{cl_sess}/u-cl1.json", 'w') as f:
f.write('{"session_id": "u-cl1", "cwd": "' + ws + '"}')
assert cline.discover(ctx_cl) == ['u-cl1']
# 5. Grok
# 4. Grok
grok = get_adapter('grok')
ctx_g = DiscoveryContext(workspace=ws, agent_name='grok', home_dir=home)
g_sess = f"{home}/.grok/sessions/{grok._ws_dir(ctx_g)}/u-g1"
@@ -350,7 +324,7 @@ def test_cli_bridge_subcommands_and_quote_safety():
assert res.stdout.strip() == "/bin/claude --dangerously-skip-permissions --session-id uuid-test"
# 3. exit-key
for agent, expected_key in [('claude', '/exit'), ('agy', 'Exit'), ('hermes', '/exit'), ('cline', '/exit'), ('grok', '/exit')]:
for agent, expected_key in [('claude', '/exit'), ('agy', 'Exit'), ('hermes', '/exit'), ('grok', '/exit')]:
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "exit-key", agent], capture_output=True, text=True, env=env)
assert res.returncode == 0
assert res.stdout.strip() == expected_key
@@ -361,7 +335,6 @@ def test_delegate_agent_resolution_and_fallback():
'claude': 'claude-code',
'agy': 'antigravity-cli',
'hermes': 'hermes-agent',
'cline': 'cline-agent',
'grok': 'grok-build',
}
# 1. Adapter property
@@ -379,7 +352,6 @@ def test_delegate_agent_resolution_and_fallback():
case "$AGENT" in
claude) delegate_agent="claude-code" ;;
hermes) delegate_agent="hermes-agent" ;;
cline) delegate_agent="cline-agent" ;;
agy) delegate_agent="antigravity-cli" ;;
grok) delegate_agent="grok-build" ;;
*) echo "ERROR: cannot resolve delegate agent key for '$AGENT'" >&2; exit 2 ;;