leerness 1.9.276 → 1.9.277
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 +25 -0
- package/README.md +9 -9
- package/bin/harness.js +92 -8
- package/package.json +1 -1
- package/scripts/e2e.js +10 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.277 — 2026-06-03 — 신규 provider 4종(opencode/qwen/aider/goose) + GPT-5.5 범용 하네스 방향 백로그 등록
|
|
4
|
+
|
|
5
|
+
**🤝 CLI 에이전트 4종을 정식 provider 로 추가 (설치 선택지 + 모델 catalog + dispatch/roles 라우팅). 빌트인 6 → 10종.**
|
|
6
|
+
|
|
7
|
+
### 신규 provider (사용자 명시)
|
|
8
|
+
1. **EXTERNAL_AGENTS +4** — `opencode`(오픈소스 터미널 에이전트) · `qwen`(Qwen Code) · `aider`(git-aware 페어프로그래밍) · `goose`(Block 범용 에이전트, MCP 확장). 각 `bin`/`envFlag`/`installCmd` 포함.
|
|
9
|
+
2. **자동 전파** — provider list(10)/cycle/setup-agents/`agents dispatch`/`roles`/`slash-commands` 가 4종 자동 흡수.
|
|
10
|
+
3. **install 흐름 10선택지** — 대화형 `_selectMany` + 비대화형 `1~10` 맵 확장.
|
|
11
|
+
4. **모델 catalog** — opencode(default/anthropic/openai) · qwen(qwen3-coder-plus/max/plus/turbo) · aider(sonnet/gpt-5/deepseek/o1-mini) · goose(default/claude/gpt).
|
|
12
|
+
5. **`_dispatchCommand`** — `opencode run` · `qwen -p` · `aider --message [--yes]` · `goose run -t` (모델 플래그 주입, best-effort).
|
|
13
|
+
6. **슬래시 레지스트리** — 4종 세션 슬래시/명령 큐레이션(opencode/qwen/aider/goose).
|
|
14
|
+
7. **config/nonsecret 키** — `LEERNESS_ENABLE_OPENCODE/QWEN/AIDER/GOOSE`.
|
|
15
|
+
|
|
16
|
+
### GPT-5.5 "범용 하네스 성장 방향" 판단 → 작업 스케줄 등록
|
|
17
|
+
GPT-5.5: leerness 는 실행 에이전트(Goose 등)와 경쟁하지 말고, 그들이 공통 의존하는 **운영 레이어(Memory+Policy+Handoff+Verification+Audit)**가 되면 범용 하네스로 성장 가능. **판단: 방향 타당 — leerness 는 이미 MCP 75도구/handoff/capabilities 로 상당 부분 정합**. 적용 항목을 백로그 등록:
|
|
18
|
+
- **UR-0030** 운영 레이어 정체성 포지셔닝 (실행기 경쟁 지양)
|
|
19
|
+
- **UR-0031** MCP 시맨틱 verb 정비 (get_context/start_task/record_decision/verify_done/make_handoff …)
|
|
20
|
+
- **UR-0032** `.leerness/` JSON 상태 스키마 (runs/tasks/decisions — 에이전트 간 인수인계 표준)
|
|
21
|
+
- **UR-0033** `leerness adapter <tool>` 도구별 선택 생성 (--minimal 확장)
|
|
22
|
+
- **UR-0034** 권한 등급 enforced (read-only…publish, 기본 보수적) — capabilities 확장
|
|
23
|
+
- **UR-0035** AGENTS.md(정적) vs leerness(동적) 역할 분리 포지셔닝
|
|
24
|
+
|
|
25
|
+
### 검증
|
|
26
|
+
- **selftest 27/27 PASS** · **E2E 224/224 PASS** (회귀 0) — agents list/quota 10 CLI · provider list 10.
|
|
27
|
+
|
|
3
28
|
## 1.9.276 — 2026-06-03 — 설치 부담 완화: init --dry-run / --minimal / --no-env (GPT-5.5 2차 리뷰)
|
|
4
29
|
|
|
5
30
|
**📦 GPT-5.5 2차 외부 평가(7/10)의 핵심 지적 "init이 너무 침투적 + 미리보기 부재"를 직접 해결.**
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
> **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []()
|
|
7
7
|
|
|
8
8
|
```
|
|
9
9
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
## 한눈에 보기
|
|
30
30
|
|
|
31
|
-
`leerness`는 **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok** 같은 AI 코딩 에이전트와 함께 일할 때 생기는 구조적 문제를 자동으로 막아주는 CLI 도구입니다.
|
|
31
|
+
`leerness`는 **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok · opencode · Qwen · Aider · Goose** 같은 AI 코딩 에이전트와 함께 일할 때 생기는 구조적 문제를 자동으로 막아주는 CLI 도구입니다.
|
|
32
32
|
|
|
33
33
|
AI는 코드를 빠르게 쓰지만, 다음 **5가지 함정**에 반복해서 빠집니다 — leerness는 이를 모두 자동으로 감지·차단·회복합니다.
|
|
34
34
|
|
|
@@ -139,7 +139,7 @@ leerness session close [path] # 세션 마감 + 다음 세션 인계
|
|
|
139
139
|
leerness drift check [path] # leerness 미사용 drift 점수
|
|
140
140
|
|
|
141
141
|
# 멀티 AI 에이전트 · 역할
|
|
142
|
-
leerness setup-agents # 활성화 (claude/codex/agy/grok/copilot/ollama)
|
|
142
|
+
leerness setup-agents # 활성화 (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
|
|
143
143
|
leerness agents list | quota # 상태 / 한도
|
|
144
144
|
leerness agents dispatch "<task>" --role coder # 역할 기반 모델 라우팅
|
|
145
145
|
leerness agents bench "<task>" # 여러 CLI 동시 호출 + 비교
|
|
@@ -215,7 +215,7 @@ leerness release channel # 현재 채널/정책 확인 (--json)
|
|
|
215
215
|
|
|
216
216
|
## At a glance
|
|
217
217
|
|
|
218
|
-
`leerness` is a CLI harness that automatically prevents the structural problems that arise when working with AI coding agents like **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok**.
|
|
218
|
+
`leerness` is a CLI harness that automatically prevents the structural problems that arise when working with AI coding agents like **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok · opencode · Qwen · Aider · Goose**.
|
|
219
219
|
|
|
220
220
|
AI writes code fast, but repeatedly falls into **5 traps** — leerness detects, blocks, and recovers from all of them.
|
|
221
221
|
|
|
@@ -326,7 +326,7 @@ leerness session close [path] # close session + hand off
|
|
|
326
326
|
leerness drift check [path] # leerness-disuse drift score
|
|
327
327
|
|
|
328
328
|
# Multi AI agents · roles
|
|
329
|
-
leerness setup-agents # enable (claude/codex/agy/grok/copilot/ollama)
|
|
329
|
+
leerness setup-agents # enable (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
|
|
330
330
|
leerness agents list | quota # status / limits
|
|
331
331
|
leerness agents dispatch "<task>" --role coder # role-based model routing
|
|
332
332
|
leerness agents bench "<task>" # call several CLIs at once + compare
|
|
@@ -440,7 +440,7 @@ MIT — © leerness contributors
|
|
|
440
440
|
<!-- leerness:project-readme:start -->
|
|
441
441
|
## Leerness Project Harness
|
|
442
442
|
|
|
443
|
-
이 프로젝트는 Leerness v1.9.
|
|
443
|
+
이 프로젝트는 Leerness v1.9.277 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
444
444
|
|
|
445
445
|
### Core Commands
|
|
446
446
|
|
|
@@ -482,7 +482,7 @@ leerness memory restore decision <date|title>
|
|
|
482
482
|
|
|
483
483
|
### MCP server (외부 AI 통합)
|
|
484
484
|
|
|
485
|
-
Leerness v1.9.
|
|
485
|
+
Leerness v1.9.277는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **42개 도구**를 노출:
|
|
486
486
|
|
|
487
487
|
```jsonc
|
|
488
488
|
// 카테고리별
|
|
@@ -503,7 +503,7 @@ Leerness v1.9.276는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
503
503
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
504
504
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push (1.9.140+) → 7) session close → 8) 다음 라운드 예약.
|
|
505
505
|
|
|
506
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
506
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.277)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
507
507
|
|
|
508
508
|
### 성능 가이드 (1.9.140 측정)
|
|
509
509
|
|
|
@@ -541,6 +541,6 @@ leerness release pack --close --auto-main-push
|
|
|
541
541
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
542
542
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
543
543
|
|
|
544
|
-
Last synced by Leerness v1.9.
|
|
544
|
+
Last synced by Leerness v1.9.277: 2026-06-03
|
|
545
545
|
<!-- leerness:project-readme:end -->
|
|
546
546
|
|
package/bin/harness.js
CHANGED
|
@@ -9,7 +9,7 @@ const readline = require('readline');
|
|
|
9
9
|
// 1.9.274 (UR-0025 1단계): 순수 유틸 함수 모듈 분리 (require-based, 비파괴). selftest 7종이 동작 검증.
|
|
10
10
|
const { _isSecretKey, compareVer, parseHarnessVersion, _classifyCJK, _riskLabel, _detectSystemLang, _parseSlashFromHelp } = require('../lib/pure-utils');
|
|
11
11
|
|
|
12
|
-
const VERSION = '1.9.
|
|
12
|
+
const VERSION = '1.9.277';
|
|
13
13
|
|
|
14
14
|
// 1.9.184: DEP0190 (child_process shell: true) deprecation warning 억제 (사용자 명시).
|
|
15
15
|
// leerness 는 cross-platform PATH resolution 을 위해 shell: true 를 의도적으로 사용 (claude.cmd / ollama.cmd 등 Windows .cmd 처리).
|
|
@@ -866,19 +866,24 @@ async function resolveInstallOptions(root, opts = {}) {
|
|
|
866
866
|
{ label: 'Codex (OpenAI codex CLI)', description: 'OpenAI 코드 모델', id: 'codex' },
|
|
867
867
|
{ label: 'Antigravity (agy CLI)', description: 'Google Antigravity 멀티모달 에이전트 (1.9.248~)', id: 'agy' },
|
|
868
868
|
{ label: 'Grok (xAI grok-cli)', description: 'xAI Grok CLI — grok-beta 등 (1.9.268~)', id: 'grok' },
|
|
869
|
+
{ label: 'opencode', description: '오픈소스 터미널 AI 코딩 에이전트 (1.9.277~)', id: 'opencode' },
|
|
870
|
+
{ label: 'Qwen Code (qwen)', description: 'Alibaba Qwen Code CLI (1.9.277~)', id: 'qwen' },
|
|
871
|
+
{ label: 'Aider', description: 'git-aware 페어 프로그래밍 CLI (1.9.277~)', id: 'aider' },
|
|
872
|
+
{ label: 'Goose (Block)', description: '오픈소스 범용 로컬 AI 에이전트 — MCP 확장 (1.9.277~)', id: 'goose' },
|
|
869
873
|
{ label: 'Copilot (gh extension)', description: 'GitHub Copilot CLI', id: 'copilot' },
|
|
870
874
|
{ label: 'Ollama (로컬 LLM — llama3/qwen 등)', description: 'http://localhost:11434 — 무료/오프라인', id: 'ollama' }
|
|
871
875
|
], { defaults: [] });
|
|
872
876
|
agentsOptIn = picked.length ? picked.map(p => p.id) : 'none';
|
|
873
877
|
} else {
|
|
874
878
|
log('\nCLI 에이전트 활성화 (복수 선택 — 콤마로 구분, opt-in):');
|
|
875
|
-
log(' 1) claude 2) codex 3) agy
|
|
879
|
+
log(' 1) claude 2) codex 3) agy 4) grok 5) opencode 6) qwen 7) aider 8) goose 9) copilot 10) ollama (예: 1,8 또는 all 또는 none)');
|
|
876
880
|
const a = (await ask('선택 [none]: ')).trim().toLowerCase();
|
|
877
|
-
|
|
881
|
+
const ALL_IDS = ['claude', 'codex', 'agy', 'grok', 'opencode', 'qwen', 'aider', 'goose', 'copilot', 'ollama'];
|
|
882
|
+
if (a === 'all') agentsOptIn = ALL_IDS.slice();
|
|
878
883
|
else if (!a || a === 'none' || a === '0') agentsOptIn = 'none';
|
|
879
884
|
else {
|
|
880
|
-
const map = { '1': 'claude', '2': 'codex', '3': 'agy', '4': 'grok', '5': '
|
|
881
|
-
const picks = a.split(/[,\s]+/).map(t => map[t] || (
|
|
885
|
+
const map = { '1': 'claude', '2': 'codex', '3': 'agy', '4': 'grok', '5': 'opencode', '6': 'qwen', '7': 'aider', '8': 'goose', '9': 'copilot', '10': 'ollama' };
|
|
886
|
+
const picks = a.split(/[,\s]+/).map(t => map[t] || (ALL_IDS.includes(t) ? t : null)).filter(Boolean);
|
|
882
887
|
agentsOptIn = picks.length ? picks : 'none';
|
|
883
888
|
}
|
|
884
889
|
}
|
|
@@ -1071,6 +1076,10 @@ async function install(root, opts = {}) {
|
|
|
1071
1076
|
LEERNESS_ENABLE_CODEX: enable('codex') ? '1' : '0',
|
|
1072
1077
|
LEERNESS_ENABLE_AGY: enable('agy') ? '1' : '0',
|
|
1073
1078
|
LEERNESS_ENABLE_GROK: enable('grok') ? '1' : '0',
|
|
1079
|
+
LEERNESS_ENABLE_OPENCODE: enable('opencode') ? '1' : '0',
|
|
1080
|
+
LEERNESS_ENABLE_QWEN: enable('qwen') ? '1' : '0',
|
|
1081
|
+
LEERNESS_ENABLE_AIDER: enable('aider') ? '1' : '0',
|
|
1082
|
+
LEERNESS_ENABLE_GOOSE: enable('goose') ? '1' : '0',
|
|
1074
1083
|
LEERNESS_ENABLE_COPILOT: enable('copilot') ? '1' : '0',
|
|
1075
1084
|
LEERNESS_ENABLE_OLLAMA: enable('ollama') ? '1' : '0',
|
|
1076
1085
|
LEERNESS_SKILL_DISCOVER_URL: '',
|
|
@@ -2944,7 +2953,7 @@ function _selfTestCases() {
|
|
|
2944
2953
|
{ name: '_agentSlashHint: grok 슬래시 요약 + copilot 하위명령 라벨', run: () => { const g = _agentSlashHint('.', 'grok'); const c = _agentSlashHint('.', 'copilot'); return !!g && g.count > 0 && /Grok/.test(g.summary) && !!c && c.invoke === 'subcommand' && /하위명령/.test(c.summary); } },
|
|
2945
2954
|
{ name: '_parseSlashFromHelp: 슬래시 검출 + 플래그 제외 (1.9.267)', run: () => { const r = _parseSlashFromHelp(' /help show help\n /model switch model\n --version print version\n', 'slash'); return r.length === 2 && r[0].cmd === '/help' && /show help/.test(r[0].desc) && !r.some(c => /version/.test(c.cmd)); } },
|
|
2946
2955
|
{ name: '_parseSlashFromHelp: subcommand 모드 들여쓰기 파싱 (1.9.267)', run: () => { const r = _parseSlashFromHelp('Commands:\n suggest 제안\n explain 설명\n', 'subcommand'); return r.length >= 2 && r.some(c => c.cmd === 'suggest') && r.some(c => c.cmd === 'explain'); } },
|
|
2947
|
-
{ name: 'EXTERNAL_AGENTS: grok
|
|
2956
|
+
{ name: 'EXTERNAL_AGENTS: 10종 (grok + opencode/qwen/aider/goose 포함) (1.9.277)', run: () => { const ids = EXTERNAL_AGENTS.map(a => a.id); return EXTERNAL_AGENTS.length === 10 && ['grok','opencode','qwen','aider','goose'].every(id => ids.includes(id)) && EXTERNAL_AGENTS.find(a => a.id === 'goose').envFlag === 'LEERNESS_ENABLE_GOOSE'; } },
|
|
2948
2957
|
{ name: '_detectSystemLang: POSIX LANG ko_KR/en_US 판별 (1.9.269)', run: () => _detectSystemLang({ LANG: 'ko_KR.UTF-8' }) === 'ko' && _detectSystemLang({ LANG: 'en_US.UTF-8' }) === 'en' },
|
|
2949
2958
|
{ name: '_detectSystemLang: LC_ALL 우선 + LANGUAGE 폴백 (1.9.269)', run: () => _detectSystemLang({ LC_ALL: 'ko_KR.UTF-8', LANG: 'en_US.UTF-8' }) === 'ko' && _detectSystemLang({ LANGUAGE: 'en_GB' }) === 'en' },
|
|
2950
2959
|
{ name: 'ROLE_CATALOG + _normalizeRole: 7종 + 한국어 별칭 (1.9.270)', run: () => { const keys = Object.keys(ROLE_CATALOG); return keys.length === 7 && keys.includes('coder') && keys.includes('reviewer') && _normalizeRole('코딩') === 'coder' && _normalizeRole('검수자') === 'reviewer' && _normalizeRole('지휘관') === 'commander'; } },
|
|
@@ -9464,6 +9473,10 @@ const _LEERNESS_NONSECRET_KEYS = new Set([
|
|
|
9464
9473
|
'LEERNESS_ENABLE_CODEX',
|
|
9465
9474
|
'LEERNESS_ENABLE_AGY',
|
|
9466
9475
|
'LEERNESS_ENABLE_GROK',
|
|
9476
|
+
'LEERNESS_ENABLE_OPENCODE',
|
|
9477
|
+
'LEERNESS_ENABLE_QWEN',
|
|
9478
|
+
'LEERNESS_ENABLE_AIDER',
|
|
9479
|
+
'LEERNESS_ENABLE_GOOSE',
|
|
9467
9480
|
'LEERNESS_ENABLE_COPILOT',
|
|
9468
9481
|
'LEERNESS_ENABLE_OLLAMA',
|
|
9469
9482
|
'LEERNESS_SKILL_DISCOVER_URL', // 공개 URL
|
|
@@ -10122,6 +10135,15 @@ const EXTERNAL_AGENTS = [
|
|
|
10122
10135
|
// → provider cycle / setup-agents / dispatch / slash-commands --refresh probe 가 grok 도 자동 처리.
|
|
10123
10136
|
{ id: 'grok', bin: 'grok', envFlag: 'LEERNESS_ENABLE_GROK', versionArgs: ['--version'], desc: 'xAI Grok CLI (커뮤니티 grok-cli, /model grok-beta 등)',
|
|
10124
10137
|
installCmd: 'npm i -g @vibe-kit/grok-cli', installHint: 'https://github.com/superagent-ai/grok-cli (xAI API 키 필요 — /login 또는 GROK_API_KEY)' },
|
|
10138
|
+
// 1.9.277 (사용자 명시): 신규 CLI 에이전트 4종 — opencode/qwen/aider/goose. 활성 시 sub-agent dispatch/roles 라우팅 대상.
|
|
10139
|
+
{ id: 'opencode', bin: 'opencode', envFlag: 'LEERNESS_ENABLE_OPENCODE', versionArgs: ['--version'], desc: 'opencode — 오픈소스 터미널 AI 코딩 에이전트 (provider-agnostic)',
|
|
10140
|
+
installCmd: 'npm i -g opencode-ai', installHint: 'https://opencode.ai (또는 curl -fsSL https://opencode.ai/install | bash)' },
|
|
10141
|
+
{ id: 'qwen', bin: 'qwen', envFlag: 'LEERNESS_ENABLE_QWEN', versionArgs: ['--version'], desc: 'Qwen Code CLI (Alibaba qwen3-coder 등)',
|
|
10142
|
+
installCmd: 'npm i -g @qwen-code/qwen-code', installHint: 'https://github.com/QwenLM/qwen-code (DASHSCOPE/OpenAI 호환 키)' },
|
|
10143
|
+
{ id: 'aider', bin: 'aider', envFlag: 'LEERNESS_ENABLE_AIDER', versionArgs: ['--version'], desc: 'Aider — git-aware 페어 프로그래밍 CLI (--model 임의 지정)',
|
|
10144
|
+
installCmd: 'python -m pip install aider-install && aider-install', installHint: 'https://aider.chat (pip install aider-chat · API 키 env)' },
|
|
10145
|
+
{ id: 'goose', bin: 'goose', envFlag: 'LEERNESS_ENABLE_GOOSE', versionArgs: ['--version'], desc: 'Goose — Block 오픈소스 범용 로컬 AI 에이전트 (MCP 확장)',
|
|
10146
|
+
installCmd: 'curl -fsSL https://github.com/block/goose/releases/latest/download/download_cli.sh | bash', installHint: 'https://block.github.io/goose (goose configure 로 provider/model 설정)' },
|
|
10125
10147
|
{ id: 'copilot', bin: 'gh', envFlag: 'LEERNESS_ENABLE_COPILOT', versionArgs: ['copilot', '--version'], desc: 'GitHub Copilot CLI (gh copilot)',
|
|
10126
10148
|
installCmd: 'gh extension install github/gh-copilot', installHint: 'https://github.com/github/gh-copilot (gh CLI 선행 설치 필요)' },
|
|
10127
10149
|
// 1.9.146: Ollama 추가 (사용자 명시 요청 #3) — 로컬 LLM, HTTP API 11434
|
|
@@ -10200,6 +10222,40 @@ const AGENT_SLASH_COMMANDS = {
|
|
|
10200
10222
|
{ cmd: 'explain', desc: '명령 설명 (gh copilot explain "...")' },
|
|
10201
10223
|
{ cmd: 'config', desc: 'gh copilot 설정' }
|
|
10202
10224
|
]
|
|
10225
|
+
},
|
|
10226
|
+
// 1.9.277: 신규 4종 슬래시/세션 명령 (best-effort 큐레이션 — 배포판마다 차이 가능, 사용자 override 권장)
|
|
10227
|
+
opencode: {
|
|
10228
|
+
label: 'opencode', asOf: '1.9.277', invoke: 'slash',
|
|
10229
|
+
note: 'TUI 세션 슬래시 — 배포판마다 차이 가능.',
|
|
10230
|
+
commands: [
|
|
10231
|
+
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/new', desc: '새 세션' }, { cmd: '/models', desc: '모델 선택' },
|
|
10232
|
+
{ cmd: '/init', desc: '프로젝트 컨텍스트' }, { cmd: '/undo', desc: '되돌리기' }, { cmd: '/share', desc: '세션 공유' }, { cmd: '/exit', desc: '종료' }
|
|
10233
|
+
]
|
|
10234
|
+
},
|
|
10235
|
+
qwen: {
|
|
10236
|
+
label: 'Qwen Code CLI', asOf: '1.9.277', invoke: 'slash',
|
|
10237
|
+
note: 'Gemini CLI 계열 슬래시(qwen-code) 기준 큐레이션.',
|
|
10238
|
+
commands: [
|
|
10239
|
+
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/clear', desc: '컨텍스트 초기화' }, { cmd: '/memory', desc: '메모리 관리' },
|
|
10240
|
+
{ cmd: '/tools', desc: '도구 목록' }, { cmd: '/model', desc: '모델 전환' }, { cmd: '/quit', desc: '종료' }
|
|
10241
|
+
]
|
|
10242
|
+
},
|
|
10243
|
+
aider: {
|
|
10244
|
+
label: 'Aider', asOf: '1.9.277', invoke: 'slash',
|
|
10245
|
+
note: 'aider 세션 내 슬래시 명령 (in-chat commands).',
|
|
10246
|
+
commands: [
|
|
10247
|
+
{ cmd: '/add', desc: '파일 컨텍스트 추가' }, { cmd: '/drop', desc: '파일 제거' }, { cmd: '/ask', desc: '코드 수정 없이 질문' },
|
|
10248
|
+
{ cmd: '/run', desc: '셸 명령 실행' }, { cmd: '/test', desc: '테스트 실행' }, { cmd: '/diff', desc: '변경 diff' },
|
|
10249
|
+
{ cmd: '/commit', desc: '커밋' }, { cmd: '/undo', desc: '마지막 커밋 취소' }, { cmd: '/help', desc: '도움말' }
|
|
10250
|
+
]
|
|
10251
|
+
},
|
|
10252
|
+
goose: {
|
|
10253
|
+
label: 'Goose (Block)', asOf: '1.9.277', invoke: 'slash',
|
|
10254
|
+
note: 'goose 세션 내 명령 (배포판마다 차이 가능).',
|
|
10255
|
+
commands: [
|
|
10256
|
+
{ cmd: '/?', desc: '도움말' }, { cmd: '/mode', desc: '모드 전환(auto/approve/chat)' }, { cmd: '/extension', desc: 'MCP 확장 관리' },
|
|
10257
|
+
{ cmd: '/builtin', desc: '빌트인 확장' }, { cmd: '/exit', desc: '종료' }
|
|
10258
|
+
]
|
|
10203
10259
|
}
|
|
10204
10260
|
};
|
|
10205
10261
|
function _agentSlashFile(root) { return path.join(absRoot(root), '.harness', 'agent-slash-commands.json'); }
|
|
@@ -10411,7 +10467,7 @@ function slashCommandsCmd(root, agentArg, opts = {}) {
|
|
|
10411
10467
|
}
|
|
10412
10468
|
|
|
10413
10469
|
// 1.9.157: Provider Registry — 사용자 정의 provider 동적 추가 (.harness/providers.json)
|
|
10414
|
-
// 빌트인
|
|
10470
|
+
// 빌트인 10종 (EXTERNAL_AGENTS: claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama) + 사용자 정의를 merge. OpenRouter / Bedrock 등 새 CLI 즉시 흡수 가능.
|
|
10415
10471
|
// 파일 형식: { "schemaVersion": 1, "providers": [{ id, bin, envFlag, versionArgs, desc, installHint }] }
|
|
10416
10472
|
function _providersFile(root) { return path.join(absRoot(root), '.harness', 'providers.json'); }
|
|
10417
10473
|
function _readUserProviders(root) {
|
|
@@ -11291,6 +11347,11 @@ function _dispatchCommand(agentId, task, writeMode, model) {
|
|
|
11291
11347
|
if (agentId === 'codex') return `codex ${writeMode ? 'exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox' : 'exec --skip-git-repo-check'}${m ? ` -m ${m}` : ''} "${q}"`;
|
|
11292
11348
|
if (agentId === 'agy') return `agy ${writeMode ? '-p --yolo' : '-p'}${m ? ` --model ${m}` : ''} "${q}"`;
|
|
11293
11349
|
if (agentId === 'grok') return `grok${writeMode ? ' --yolo' : ''}${m ? ` --model ${m}` : ''} "${q}"`;
|
|
11350
|
+
// 1.9.277: 신규 provider 4종 — best-effort 비대화형 호출 (실제 실행은 사용자/메인)
|
|
11351
|
+
if (agentId === 'opencode') return `opencode run${m ? ` --model ${m}` : ''} "${q}"`;
|
|
11352
|
+
if (agentId === 'qwen') return `qwen -p "${q}"${m ? ` # --model ${m} (env/설정)` : ''}`;
|
|
11353
|
+
if (agentId === 'aider') return `aider --message "${q}"${writeMode ? ' --yes' : ' --no-auto-commits'}${m ? ` --model ${m}` : ''}`;
|
|
11354
|
+
if (agentId === 'goose') return `goose run -t "${q}"${m && m !== 'default' ? ` # provider/model: goose configure` : ''}`;
|
|
11294
11355
|
if (agentId === 'copilot') return `gh copilot suggest "${q}"`;
|
|
11295
11356
|
if (agentId === 'ollama') return `# ollama — leerness agent "${q}" --provider ollama${m ? ` --model ${m}` : ''} 로 직접 호출 (REPL: leerness agent)`;
|
|
11296
11357
|
return `# ${agentId}: 명령 빌더 미정의`;
|
|
@@ -17678,6 +17739,29 @@ const _PROVIDER_MODEL_CATALOG = {
|
|
|
17678
17739
|
{ id: 'grok-2', note: 'xAI Grok 2' },
|
|
17679
17740
|
{ id: 'grok-2-mini', note: '빠른 응답' }
|
|
17680
17741
|
],
|
|
17742
|
+
// 1.9.277: 신규 provider 4종 모델 (provider-agnostic 인 도구는 default + 설정 안내)
|
|
17743
|
+
opencode: [
|
|
17744
|
+
{ id: 'default', note: 'opencode 설정 모델 (opencode auth/models 로 provider별 지정)' },
|
|
17745
|
+
{ id: 'anthropic/claude-sonnet', note: 'Anthropic 경유' },
|
|
17746
|
+
{ id: 'openai/gpt-5', note: 'OpenAI 경유' }
|
|
17747
|
+
],
|
|
17748
|
+
qwen: [
|
|
17749
|
+
{ id: 'qwen3-coder-plus', note: '코드 특화 최신' },
|
|
17750
|
+
{ id: 'qwen-max', note: '최고 성능' },
|
|
17751
|
+
{ id: 'qwen-plus', note: '균형' },
|
|
17752
|
+
{ id: 'qwen-turbo', note: '빠름' }
|
|
17753
|
+
],
|
|
17754
|
+
aider: [
|
|
17755
|
+
{ id: 'sonnet', note: 'Anthropic Sonnet (aider --model)' },
|
|
17756
|
+
{ id: 'gpt-5', note: 'OpenAI' },
|
|
17757
|
+
{ id: 'deepseek', note: 'DeepSeek (가성비)' },
|
|
17758
|
+
{ id: 'o1-mini', note: '빠른 reasoning' }
|
|
17759
|
+
],
|
|
17760
|
+
goose: [
|
|
17761
|
+
{ id: 'default', note: 'goose configure 로 설정한 provider/model' },
|
|
17762
|
+
{ id: 'claude-sonnet', note: 'Anthropic 경유' },
|
|
17763
|
+
{ id: 'gpt-5', note: 'OpenAI 경유' }
|
|
17764
|
+
],
|
|
17681
17765
|
copilot: [
|
|
17682
17766
|
{ id: 'default', note: 'gh copilot 기본 (모델 선택 불가)' }
|
|
17683
17767
|
],
|
|
@@ -17690,7 +17774,7 @@ const _PROVIDER_MODEL_CATALOG = {
|
|
|
17690
17774
|
};
|
|
17691
17775
|
|
|
17692
17776
|
// 1.9.170: provider cycle 순서 (Tab) — 빌트인 6종(1.9.268 grok 추가). user provider는 동적으로 뒤에 추가.
|
|
17693
|
-
const _PROVIDER_CYCLE_ORDER = ['ollama', 'claude', 'codex', 'agy', 'grok', 'copilot'];
|
|
17777
|
+
const _PROVIDER_CYCLE_ORDER = ['ollama', 'claude', 'codex', 'agy', 'grok', 'opencode', 'qwen', 'aider', 'goose', 'copilot'];
|
|
17694
17778
|
|
|
17695
17779
|
// 1.9.148: planner/reviewer/actor 역할 시스템 프롬프트 (Gemini 권고 — 자기-승인 편향 방지)
|
|
17696
17780
|
const _AGENT_ROLE_PROMPTS = {
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -807,14 +807,16 @@ total++;
|
|
|
807
807
|
&& /\| grok \|/.test(r1.stdout)
|
|
808
808
|
&& /\| copilot \|/.test(r1.stdout);
|
|
809
809
|
// env 모두 0 → 비활성
|
|
810
|
-
// 1.9.146: Ollama
|
|
811
|
-
const
|
|
810
|
+
// 1.9.146: Ollama → 5 · 1.9.268: grok → 6 · 1.9.277: opencode/qwen/aider/goose → 10 CLI
|
|
811
|
+
const offAll = { LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_GROK: '0', LEERNESS_ENABLE_OPENCODE: '0', LEERNESS_ENABLE_QWEN: '0', LEERNESS_ENABLE_AIDER: '0', LEERNESS_ENABLE_GOOSE: '0', LEERNESS_ENABLE_COPILOT: '0', LEERNESS_ENABLE_OLLAMA: '0' };
|
|
812
|
+
const env2 = { ...process.env, ...offAll };
|
|
812
813
|
const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list', '--json'], { encoding: 'utf8', timeout: 15000, env: env2 });
|
|
813
814
|
let parsed = null;
|
|
814
815
|
try { parsed = JSON.parse(r2.stdout); } catch {}
|
|
815
|
-
const okJson = parsed && Array.isArray(parsed.agents) && parsed.agents.length ===
|
|
816
|
-
const
|
|
817
|
-
|
|
816
|
+
const okJson = parsed && Array.isArray(parsed.agents) && parsed.agents.length === 10 && parsed.agents.every(a => a.status !== 'ready');
|
|
817
|
+
const okNew = /\| opencode \|/.test(r1.stdout) && /\| qwen \|/.test(r1.stdout) && /\| aider \|/.test(r1.stdout) && /\| goose \|/.test(r1.stdout);
|
|
818
|
+
const ok = okList && okJson && okNew;
|
|
819
|
+
console.log(ok ? '✓ B(1.9.30+1.9.277) agents list: 10 CLI (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)' : `✗ agents list 실패 (list=${okList} json=${okJson} new=${okNew})`);
|
|
818
820
|
if (!ok) { failed++; console.log(r1.stdout.slice(0, 500)); }
|
|
819
821
|
}
|
|
820
822
|
|
|
@@ -853,11 +855,11 @@ total++;
|
|
|
853
855
|
const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'quota', '--json'], { encoding: 'utf8', timeout: 15000, env });
|
|
854
856
|
let parsed = null;
|
|
855
857
|
try { parsed = JSON.parse(r2.stdout); } catch {}
|
|
856
|
-
// 1.9.
|
|
857
|
-
const okJson = parsed && Array.isArray(parsed.quota) && parsed.quota.length ===
|
|
858
|
+
// 1.9.277: opencode/qwen/aider/goose → 10 CLI
|
|
859
|
+
const okJson = parsed && Array.isArray(parsed.quota) && parsed.quota.length === 10
|
|
858
860
|
&& parsed.quota.every(q => typeof q.id === 'string' && typeof q.status === 'string' && (q.hint === null || typeof q.hint === 'string'));
|
|
859
861
|
const ok = okText && okJson;
|
|
860
|
-
console.log(ok ? '✓ B(1.9.31+1.9.
|
|
862
|
+
console.log(ok ? '✓ B(1.9.31+1.9.277) agents quota: 10 CLI 사용량/안내' : `✗ quota 실패 (text=${okText} json=${okJson})`);
|
|
861
863
|
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
862
864
|
}
|
|
863
865
|
|