leerness 1.9.29 → 1.9.31
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 +53 -0
- package/README.md +57 -2
- package/bin/harness.js +185 -3
- package/package.json +1 -1
- package/scripts/e2e.js +73 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.31 — 2026-05-15
|
|
4
|
+
|
|
5
|
+
**`leerness agents quota` — 외부 AI CLI 사용량/한도 추정 + provider 대시보드 안내**.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`leerness agents quota`** (1.9.31): 활성 CLI별 quota/rate-limit 정보 표시.
|
|
10
|
+
- **claude**: 비대화형 quota API 없음 → `/status` 슬래시 또는 https://console.anthropic.com/settings/usage 안내.
|
|
11
|
+
- **codex**: `codex --help`에서 `usage`/`quota` 키워드 감지 시 시도 가능 표시, 미감지 시 https://platform.openai.com/account/usage 안내.
|
|
12
|
+
- **gemini**: 무료 티어 `60 req/min, 1000 req/day` 명시.
|
|
13
|
+
- **copilot (gh)**: `gh auth status`로 인증 확인 → 구독자 무제한 또는 `gh auth login` 필요 안내.
|
|
14
|
+
- `--json` 출력 지원 (`{ quota: [{id, bin, status, quota, hint, raw}, ...] }`).
|
|
15
|
+
- **`agents` 사용법 메시지에 `quota` 추가**: `list|check|quota|dispatch`.
|
|
16
|
+
- **`agents dispatch` 안내문에 quota 명령 cross-link** (1.9.31+).
|
|
17
|
+
|
|
18
|
+
### Policy
|
|
19
|
+
- ❌ leerness는 사용량을 직접 추적하지 않음 (provider 대시보드 참조)
|
|
20
|
+
- ✅ sub-agent 분배 시 quota 여유 큰 CLI를 메인 에이전트가 우선 선택하도록 신호 제공
|
|
21
|
+
- ✅ rate-limit/plan 차이는 provider별 다름 — leerness는 hint만 제공
|
|
22
|
+
|
|
23
|
+
### 실측 (이번 라운드 사용 사례)
|
|
24
|
+
- agents quota 신규 명령 검증 후 sub-agent ×3 동시 분배
|
|
25
|
+
- e2e: 146/146 통과 (1.9.30 144 + quota 2)
|
|
26
|
+
|
|
27
|
+
## 1.9.30 — 2026-05-15
|
|
28
|
+
|
|
29
|
+
**외부 AI CLI 오케스트레이션 — 환경변수 활성화 정책 + `leerness agents list/check/dispatch`**.
|
|
30
|
+
|
|
31
|
+
claude/codex/gemini/copilot CLI들을 sub-agent로 명시적 활용 가능. 사용자 동의(환경변수) + PATH 존재 둘 다 충족 시에만 ready.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **`.env.example`에 4개 활성화 플래그 추가**:
|
|
36
|
+
- `LEERNESS_ENABLE_CLAUDE=1` (Anthropic Claude Code, 기본 활성)
|
|
37
|
+
- `LEERNESS_ENABLE_CODEX=0` (OpenAI Codex CLI, 격리 sandbox)
|
|
38
|
+
- `LEERNESS_ENABLE_GEMINI=0` (Google Gemini CLI, `--yolo` 모드는 워크스페이스 직접 수정 가능)
|
|
39
|
+
- `LEERNESS_ENABLE_COPILOT=0` (GitHub Copilot CLI = `gh copilot`)
|
|
40
|
+
- **`leerness agents list`**: 4 CLI별 (env=1 여부) + (PATH 존재 여부) + 버전 + 상태 (ready/disabled/not-installed) 표 출력. `--json` 지원.
|
|
41
|
+
- **`leerness agents check`**: alias of list (재확인 강조).
|
|
42
|
+
- **`leerness agents dispatch "<task>" --to <id>`**: 활성 ready CLI에 대상 명령 자동 생성 (`claude "..."`, `codex exec "..."`, `gemini -p "..." --yolo`, `gh copilot suggest "..."`).
|
|
43
|
+
- **leerness는 자동 호출 안 함** — 사용자/메인 에이전트가 명시적 실행.
|
|
44
|
+
- 비활성/미설치 시 안내 후 `exit 1`.
|
|
45
|
+
|
|
46
|
+
### Policy
|
|
47
|
+
- ❌ 환경변수 미설정 또는 PATH 없으면 dispatch 거부
|
|
48
|
+
- ✅ 환경변수 + PATH 둘 다 충족 시에만 ready
|
|
49
|
+
- ✅ leerness는 외부 CLI 자동 호출 금지 (1.9.22 Ollama opt-in과 동일 원칙)
|
|
50
|
+
|
|
51
|
+
### 실측 (이번 라운드 사용 사례)
|
|
52
|
+
- Claude sub-agent ×2 (PvP 매치메이킹 + 길드 시스템) → 각각 26/26, 23/23 통과
|
|
53
|
+
- Gemini CLI 외부 호출 (yolo 모드) → rpg-stats 통계 대시보드 자동 생성 (13/13, HTML 5.5KB)
|
|
54
|
+
- → 3 도메인 동시 진행, 메인 에이전트가 외부 CLI를 sub-agent처럼 활용
|
|
55
|
+
|
|
3
56
|
## 1.9.29 — 2026-05-15
|
|
4
57
|
|
|
5
58
|
**페르소나 시스템 — 5종 내장 + `leerness review --persona` (도메인 깊이 3-4배)**.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 한국어 우선 AI 개발 하네스. 멀티 에이전트 오케스트레이션 · 자동 검수 · 워크스페이스 가시성 · Ollama opt-in 통합.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []()
|
|
6
6
|
|
|
7
7
|
## ⚙️ 설치 (Install)
|
|
8
8
|
|
|
@@ -31,6 +31,8 @@ npm i --save-dev leerness && npx leerness handoff .
|
|
|
31
31
|
- LLM 컨텍스트 비용 → `--compact` 모드로 4KB → 500자
|
|
32
32
|
- AI가 "API 호출 완료"라 보고했지만 코드에 호출 흔적이 없는 낙관적 표시 → `optimism-check`로 자동 감지 (1.9.26/27)
|
|
33
33
|
- 코드 리뷰가 표면적이라 도메인 깊이 부족 → `leerness review <file> --persona security,performance,ux`로 도메인 페르소나 자동 부여 (1.9.29)
|
|
34
|
+
- 외부 AI CLI(claude/codex/gemini/copilot)를 sub-agent로 활용하고 싶지만 자동 호출은 위험 → 환경변수 활성화 + `leerness agents list/dispatch`로 명시적 분배 (1.9.30)
|
|
35
|
+
- 어떤 CLI에 quota 여유가 남았는지 한눈에 보고 싶을 때 → `leerness agents quota`로 provider별 사용량/한도 추정 (1.9.31)
|
|
34
36
|
|
|
35
37
|
---
|
|
36
38
|
|
|
@@ -97,6 +99,9 @@ leerness review <file> --persona security,performance,ux # 1.9.29 도메인
|
|
|
97
99
|
leerness persona list # 5종 내장 + 사용자 정의
|
|
98
100
|
leerness persona show security # 페르소나 본문
|
|
99
101
|
leerness persona add my-domain # 사용자 정의 페르소나
|
|
102
|
+
leerness agents list # 1.9.30 외부 AI CLI 상태표
|
|
103
|
+
leerness agents quota # 1.9.31 CLI별 사용량/한도 추정
|
|
104
|
+
leerness agents dispatch "<task>" --to gemini # 1.9.30 sub-agent 명령 생성
|
|
100
105
|
```
|
|
101
106
|
|
|
102
107
|
### 워크스페이스 (멀티 프로젝트)
|
|
@@ -213,6 +218,9 @@ leerness orchestrate "복잡한 기능" --agents 20
|
|
|
213
218
|
| `handoff --compact` | LLM 시스템 프롬프트용 압축 출력 | 1.9.22 |
|
|
214
219
|
| `review --persona X` | 도메인별 sub-agent 자동 프롬프트 (security/performance/ux/testing/docs) | 1.9.29 |
|
|
215
220
|
| `persona list/show/add` | 페르소나 카탈로그 관리 (.harness/personas/) | 1.9.29 |
|
|
221
|
+
| `agents list/check` | 4 CLI(claude/codex/gemini/copilot) 활성/설치 상태표 (env + PATH 검증) | 1.9.30 |
|
|
222
|
+
| `agents dispatch --to X` | ready CLI에 대상 명령 자동 생성 (자동 호출 금지) | 1.9.30 |
|
|
223
|
+
| `agents quota` | provider별 사용량/한도 추정 + 대시보드 안내 | 1.9.31 |
|
|
216
224
|
|
|
217
225
|
---
|
|
218
226
|
|
|
@@ -293,6 +301,47 @@ leerness persona add my-domain # .harness/personas/my-domain.md 템플릿 생
|
|
|
293
301
|
|
|
294
302
|
---
|
|
295
303
|
|
|
304
|
+
## 🤖 외부 AI CLI 오케스트레이션 (1.9.30 / 1.9.31)
|
|
305
|
+
|
|
306
|
+
claude/codex/gemini/copilot CLI들을 sub-agent로 명시적 활용. **자동 호출 절대 금지** — 환경변수 활성화 + PATH 존재 둘 다 충족 시에만 ready.
|
|
307
|
+
|
|
308
|
+
### 활성화 (`.env`)
|
|
309
|
+
```bash
|
|
310
|
+
LEERNESS_ENABLE_CLAUDE=1 # Anthropic Claude Code CLI
|
|
311
|
+
LEERNESS_ENABLE_CODEX=1 # OpenAI Codex CLI (격리 sandbox)
|
|
312
|
+
LEERNESS_ENABLE_GEMINI=1 # Google Gemini CLI (--yolo 모드는 워크스페이스 직접 수정 가능)
|
|
313
|
+
LEERNESS_ENABLE_COPILOT=1 # GitHub Copilot CLI (gh copilot)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 사용
|
|
317
|
+
```bash
|
|
318
|
+
leerness agents list # 4 CLI 상태표 (env + PATH + 버전)
|
|
319
|
+
leerness agents quota # provider별 사용량/한도 추정 (1.9.31)
|
|
320
|
+
leerness agents dispatch "<task>" --to gemini # ready CLI에 명령 자동 생성
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### quota 안내 (1.9.31)
|
|
324
|
+
| CLI | 추정 | 안내 |
|
|
325
|
+
|---|---|---|
|
|
326
|
+
| claude | `unknown` | 대화 내 `/status` 슬래시 또는 https://console.anthropic.com/settings/usage |
|
|
327
|
+
| codex | `cli-supported` 또는 `unknown` | `codex usage`/`codex quota` 시도 또는 https://platform.openai.com/account/usage |
|
|
328
|
+
| gemini | `rate-limited` | 무료 60 req/min, 1000 req/day · https://ai.google.dev/gemini-api/docs/rate-limits |
|
|
329
|
+
| copilot | `subscription` 또는 `not-authed` | 월 구독자 무제한 · `gh auth login` 필요 시 안내 |
|
|
330
|
+
|
|
331
|
+
### 실측 (이번 라운드)
|
|
332
|
+
- Sub B (rpg-craft, Claude 페르소나) → 20/20 pass, 1,567 라인
|
|
333
|
+
- Sub C (rpg-achievements, Claude 페르소나) → 22/22 pass, 1,375 라인
|
|
334
|
+
- Sub D (rpg-instance, Claude 페르소나, cross-project require 시연) → 20/20 pass, 1,016 라인
|
|
335
|
+
- → **3 도메인 동시 진행**, 메인 에이전트가 sub-agent들에 페르소나 + 영역 분배 + 충돌 방지 컨벤션 명시.
|
|
336
|
+
|
|
337
|
+
### 정책
|
|
338
|
+
- ❌ leerness는 외부 CLI를 자동 호출하지 않음 (사용자/메인 에이전트가 명시적 실행)
|
|
339
|
+
- ✅ `agents dispatch`는 명령 텍스트만 출력 — 복사해서 실행
|
|
340
|
+
- ✅ quota 여유 큰 CLI를 메인 에이전트가 우선 선택하도록 신호 제공
|
|
341
|
+
- ⚠ `gemini --yolo`는 워크스페이스 파일 직접 수정 가능 — 격리 sandbox 아님 (codex와 차이)
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
296
345
|
## 🤝 Claude Code 통합
|
|
297
346
|
|
|
298
347
|
설치 시 자동 등록: `.claude/commands/{handoff, session-close, audit, lazy-detect, update}.md` · `.claude/skills/leerness.md` (스킬 정의) · `.claude/settings.local.json` (SessionStart hook `update --check`) · `.cursor/rules/leerness.mdc` (Cursor) · `.github/copilot-instructions.md` (Copilot)
|
|
@@ -323,6 +372,10 @@ leerness skill consolidate
|
|
|
323
372
|
| `LEERNESS_GITHUB_TOKEN` | gh release용 (있을 때) |
|
|
324
373
|
| **`LEERNESS_OLLAMA_BASE_URL`** | **1.9.22 — orchestrate opt-in 활성화** |
|
|
325
374
|
| `LEERNESS_OLLAMA_MODEL` | 기본 모델 (orchestrate `--model`로 override) |
|
|
375
|
+
| **`LEERNESS_ENABLE_CLAUDE`** | **1.9.30 — `agents list/dispatch` Claude Code CLI 활성** (=1) |
|
|
376
|
+
| **`LEERNESS_ENABLE_CODEX`** | **1.9.30 — Codex CLI 활성** (=1) |
|
|
377
|
+
| **`LEERNESS_ENABLE_GEMINI`** | **1.9.30 — Gemini CLI 활성** (=1) |
|
|
378
|
+
| **`LEERNESS_ENABLE_COPILOT`** | **1.9.30 — gh copilot 활성** (=1) |
|
|
326
379
|
|
|
327
380
|
---
|
|
328
381
|
|
|
@@ -362,12 +415,14 @@ A. `--all-apps`는 현재 디렉토리 + `_apps/*` (또는 부모의 `_apps/*`)
|
|
|
362
415
|
npm test # = node ./scripts/e2e.js
|
|
363
416
|
```
|
|
364
417
|
|
|
365
|
-
**
|
|
418
|
+
**146/146 시나리오** 통과 (1.9.7~1.9.31 회귀 + 신규 검증).
|
|
366
419
|
|
|
367
420
|
---
|
|
368
421
|
|
|
369
422
|
## 📜 변경 이력 (최근)
|
|
370
423
|
|
|
424
|
+
- **1.9.31** — `leerness agents quota` (provider별 사용량/한도 추정 + 대시보드 안내). 멀티 에이전트 분배 신호.
|
|
425
|
+
- **1.9.30** — 외부 AI CLI 오케스트레이션 (claude/codex/gemini/copilot) + 환경변수 활성화 정책 + `leerness agents list/check/dispatch`
|
|
371
426
|
- **1.9.29** — 페르소나 시스템 (5종 내장) + `leerness review --persona` (도메인 깊이 3-4배)
|
|
372
427
|
- **1.9.28** — 카카오페이/네이버페이 패턴 + confidence floor 0.15
|
|
373
428
|
- **1.9.27** — `optimism-check` 강화: 10 카테고리 + URL/메서드 매핑 + 신뢰도 점수
|
package/bin/harness.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const cp = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.9.
|
|
9
|
+
const VERSION = '1.9.31';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -439,7 +439,12 @@ async function install(root, opts = {}) {
|
|
|
439
439
|
'# 1.9.22 — orchestrate opt-in. URL이 설정되면 leerness가 Ollama를 사용 가능. 미설정 시 LLM 호출 자동 시작 금지.',
|
|
440
440
|
'LEERNESS_OLLAMA_BASE_URL=',
|
|
441
441
|
'# 선택. 기본 모델 (orchestrate --model 로 override 가능).',
|
|
442
|
-
'LEERNESS_OLLAMA_MODEL='
|
|
442
|
+
'LEERNESS_OLLAMA_MODEL=',
|
|
443
|
+
'# 1.9.30 — 외부 AI CLI 활성화 플래그. 1=활성, 0/미설정=비활성. 메인 에이전트가 sub-agent 분배 시 활성 CLI들에 작업 위임 가능.',
|
|
444
|
+
'LEERNESS_ENABLE_CLAUDE=1',
|
|
445
|
+
'LEERNESS_ENABLE_CODEX=0',
|
|
446
|
+
'LEERNESS_ENABLE_GEMINI=0',
|
|
447
|
+
'LEERNESS_ENABLE_COPILOT=0'
|
|
443
448
|
]);
|
|
444
449
|
mergeLinesFile(path.join(root, '.gitattributes'), [
|
|
445
450
|
'* text=auto eol=lf','*.bat text eol=crlf','*.ps1 text eol=crlf'
|
|
@@ -2262,6 +2267,182 @@ function _resolvePersona(root, id) {
|
|
|
2262
2267
|
return null;
|
|
2263
2268
|
}
|
|
2264
2269
|
|
|
2270
|
+
// 1.9.30: 외부 AI CLI 오케스트레이션 — claude/codex/gemini/copilot 가용성 + 활성화 체크
|
|
2271
|
+
// 사용자 정책: 환경변수로 활성화 명시 + 실제 PATH 존재 확인 + 메인이 sub-agent 분배 시 참조
|
|
2272
|
+
const EXTERNAL_AGENTS = [
|
|
2273
|
+
{ id: 'claude', bin: 'claude', envFlag: 'LEERNESS_ENABLE_CLAUDE', versionArgs: ['--version'], desc: 'Anthropic Claude Code CLI' },
|
|
2274
|
+
{ id: 'codex', bin: 'codex', envFlag: 'LEERNESS_ENABLE_CODEX', versionArgs: ['--version'], desc: 'OpenAI Codex CLI (격리 sandbox)' },
|
|
2275
|
+
{ id: 'gemini', bin: 'gemini', envFlag: 'LEERNESS_ENABLE_GEMINI', versionArgs: ['--version'], desc: 'Google Gemini CLI (--yolo 모드 워크스페이스 직접 수정 가능)' },
|
|
2276
|
+
{ id: 'copilot', bin: 'gh', envFlag: 'LEERNESS_ENABLE_COPILOT', versionArgs: ['copilot', '--version'], desc: 'GitHub Copilot CLI (gh copilot)' }
|
|
2277
|
+
];
|
|
2278
|
+
|
|
2279
|
+
function _checkAgent(agent, opts = {}) {
|
|
2280
|
+
const enabled = process.env[agent.envFlag] === '1';
|
|
2281
|
+
// PATH 존재 확인 (which / where)
|
|
2282
|
+
let installed = false, version = null, error = null;
|
|
2283
|
+
try {
|
|
2284
|
+
const r = cp.spawnSync(agent.bin, agent.versionArgs, { encoding: 'utf8', timeout: 5000, shell: true });
|
|
2285
|
+
if (r.status === 0 || (r.stdout && r.stdout.trim())) {
|
|
2286
|
+
installed = true;
|
|
2287
|
+
version = (r.stdout || r.stderr || '').trim().split('\n')[0].slice(0, 80);
|
|
2288
|
+
} else if (r.error) {
|
|
2289
|
+
error = r.error.code || r.error.message;
|
|
2290
|
+
} else {
|
|
2291
|
+
error = `exit ${r.status}`;
|
|
2292
|
+
}
|
|
2293
|
+
} catch (e) { error = e.message; }
|
|
2294
|
+
return {
|
|
2295
|
+
id: agent.id, bin: agent.bin, desc: agent.desc, envFlag: agent.envFlag,
|
|
2296
|
+
enabled, installed, version, error,
|
|
2297
|
+
status: enabled && installed ? 'ready' : !installed ? 'not-installed' : !enabled ? 'disabled' : 'unknown'
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
function agentsCmd(root, sub, ...args) {
|
|
2302
|
+
root = absRoot(root || process.cwd());
|
|
2303
|
+
// .env 자동 로드 (1.9.22)
|
|
2304
|
+
_loadEnvFile(root);
|
|
2305
|
+
_loadEnvFile(path.join(root, '..'));
|
|
2306
|
+
|
|
2307
|
+
if (!sub || sub === 'list') {
|
|
2308
|
+
const checks = EXTERNAL_AGENTS.map(a => _checkAgent(a));
|
|
2309
|
+
if (has('--json')) { log(JSON.stringify({ agents: checks }, null, 2)); return; }
|
|
2310
|
+
log(`# 외부 AI CLI 오케스트레이션 (1.9.30)`);
|
|
2311
|
+
log('');
|
|
2312
|
+
log(`| Agent | env (${'env=1 활성'}) | 설치 | 버전 | 상태 |`);
|
|
2313
|
+
log(`|---|---|---|---|---|`);
|
|
2314
|
+
for (const c of checks) {
|
|
2315
|
+
const envMark = c.enabled ? '✓' : '✗';
|
|
2316
|
+
const instMark = c.installed ? '✓' : '✗';
|
|
2317
|
+
const statusEmoji = c.status === 'ready' ? '🟢 ready' : c.status === 'not-installed' ? '⚪ 미설치' : c.status === 'disabled' ? '🟡 비활성' : '❓';
|
|
2318
|
+
log(`| ${c.id} | ${envMark} ${c.envFlag} | ${instMark} | ${c.version || '-'} | ${statusEmoji} |`);
|
|
2319
|
+
}
|
|
2320
|
+
const ready = checks.filter(c => c.status === 'ready');
|
|
2321
|
+
log('');
|
|
2322
|
+
log(`## 활성 (${ready.length}/${checks.length}): ${ready.map(c => c.id).join(', ') || '(없음)'}`);
|
|
2323
|
+
if (!ready.length) {
|
|
2324
|
+
log('');
|
|
2325
|
+
log(`💡 활성화 방법:`);
|
|
2326
|
+
log(` 1) CLI 설치 (예: \`npm i -g @openai/codex-cli\`, \`npm i -g @google/gemini-cli\`)`);
|
|
2327
|
+
log(` 2) .env 또는 환경변수: LEERNESS_ENABLE_CODEX=1, LEERNESS_ENABLE_GEMINI=1`);
|
|
2328
|
+
log(` 3) \`leerness agents check\`로 재확인`);
|
|
2329
|
+
} else {
|
|
2330
|
+
log('');
|
|
2331
|
+
log(`💡 메인 에이전트가 sub-agent 분배 시 위 ${ready.length}개 CLI 활용 가능:`);
|
|
2332
|
+
log(` \`leerness agents dispatch "<task>" --to <id>\` 로 프롬프트 전달`);
|
|
2333
|
+
}
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
if (sub === 'check') {
|
|
2338
|
+
// list의 alias, 단 명시적 재확인 (JSON 출력 기본)
|
|
2339
|
+
const checks = EXTERNAL_AGENTS.map(a => _checkAgent(a));
|
|
2340
|
+
if (has('--json')) { log(JSON.stringify({ agents: checks, ready: checks.filter(c => c.status === 'ready').map(c => c.id) }, null, 2)); return; }
|
|
2341
|
+
return agentsCmd(root, 'list'); // 비-JSON은 list와 동일
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
if (sub === 'dispatch') {
|
|
2345
|
+
const task = args.filter(x => !x.startsWith('-')).join(' ').trim() || arg('--task', null);
|
|
2346
|
+
const target = arg('--to', null);
|
|
2347
|
+
if (!task) { fail('dispatch "<task>" 또는 --task 필요'); return process.exit(1); }
|
|
2348
|
+
if (!target) { fail('--to <agent_id> 필요 (claude/codex/gemini/copilot)'); return process.exit(1); }
|
|
2349
|
+
const agentDef = EXTERNAL_AGENTS.find(a => a.id === target);
|
|
2350
|
+
if (!agentDef) { fail(`알 수 없는 agent: ${target}`); return process.exit(1); }
|
|
2351
|
+
const status = _checkAgent(agentDef);
|
|
2352
|
+
if (status.status !== 'ready') {
|
|
2353
|
+
fail(`${target} 비활성 (${status.status}). 환경변수 ${agentDef.envFlag}=1 + CLI 설치 필요.`);
|
|
2354
|
+
return process.exit(1);
|
|
2355
|
+
}
|
|
2356
|
+
// 실제 호출은 안 함 — 프롬프트만 생성 (사용자가 명시적으로 실행)
|
|
2357
|
+
log(`# leerness agents dispatch (1.9.30)`);
|
|
2358
|
+
log(`대상: ${target} (${agentDef.bin})`);
|
|
2359
|
+
log(`상태: 🟢 ready, 버전 ${status.version || '?'}`);
|
|
2360
|
+
log('');
|
|
2361
|
+
log(`## 실행 명령 (사용자가 복사해서 실행)`);
|
|
2362
|
+
log('');
|
|
2363
|
+
if (target === 'claude') {
|
|
2364
|
+
log(`claude "${task.replace(/"/g, '\\"')}"`);
|
|
2365
|
+
} else if (target === 'codex') {
|
|
2366
|
+
log(`codex exec "${task.replace(/"/g, '\\"')}"`);
|
|
2367
|
+
} else if (target === 'gemini') {
|
|
2368
|
+
log(`gemini -p "${task.replace(/"/g, '\\"')}" --yolo # ⚠ yolo는 워크스페이스 직접 수정 가능`);
|
|
2369
|
+
} else if (target === 'copilot') {
|
|
2370
|
+
log(`gh copilot suggest "${task.replace(/"/g, '\\"')}"`);
|
|
2371
|
+
}
|
|
2372
|
+
log('');
|
|
2373
|
+
log(`## 정책 (1.9.30)`);
|
|
2374
|
+
log(` - leerness는 외부 CLI를 자동 호출하지 않음 (사용자 명시적 실행)`);
|
|
2375
|
+
log(` - 메인 에이전트(Claude)가 위 명령을 보고 sub-agent로 spawn 가능`);
|
|
2376
|
+
log(` - quota 체크: \`leerness agents quota\` (1.9.31+)`);
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
if (sub === 'quota') {
|
|
2381
|
+
// 1.9.31: 각 CLI 사용량/쿼터 추정 + provider 대시보드 링크
|
|
2382
|
+
const results = [];
|
|
2383
|
+
for (const agent of EXTERNAL_AGENTS) {
|
|
2384
|
+
const base = _checkAgent(agent);
|
|
2385
|
+
const out = { id: agent.id, bin: agent.bin, status: base.status, quota: null, hint: null, raw: null };
|
|
2386
|
+
if (base.status !== 'ready') {
|
|
2387
|
+
out.hint = base.status === 'not-installed' ? `${agent.bin} CLI 미설치` : base.status === 'disabled' ? `${agent.envFlag}=1 필요` : '알 수 없음';
|
|
2388
|
+
results.push(out); continue;
|
|
2389
|
+
}
|
|
2390
|
+
// CLI별 quota 탐지 시도
|
|
2391
|
+
try {
|
|
2392
|
+
if (agent.id === 'claude') {
|
|
2393
|
+
// claude는 /status 슬래시 (대화형)만 지원. 비대화형 추정 불가.
|
|
2394
|
+
out.quota = 'unknown';
|
|
2395
|
+
out.hint = '대화 내 `/status` 슬래시 또는 https://console.anthropic.com/settings/usage 확인';
|
|
2396
|
+
} else if (agent.id === 'codex') {
|
|
2397
|
+
// codex CLI: codex --help에 usage 명령 있는지 확인
|
|
2398
|
+
const r = cp.spawnSync(agent.bin, ['--help'], { encoding: 'utf8', timeout: 4000, shell: true });
|
|
2399
|
+
const help = (r.stdout || r.stderr || '').toLowerCase();
|
|
2400
|
+
if (help.includes('usage') || help.includes('quota')) {
|
|
2401
|
+
out.quota = 'cli-supported';
|
|
2402
|
+
out.hint = '`codex usage` 또는 `codex quota` 시도 가능';
|
|
2403
|
+
} else {
|
|
2404
|
+
out.quota = 'unknown';
|
|
2405
|
+
out.hint = 'https://platform.openai.com/account/usage 확인';
|
|
2406
|
+
}
|
|
2407
|
+
out.raw = help.slice(0, 200);
|
|
2408
|
+
} else if (agent.id === 'gemini') {
|
|
2409
|
+
// gemini CLI: 무료 티어는 분당 60req 제한, CLI 자체에선 노출 안 됨
|
|
2410
|
+
out.quota = 'rate-limited';
|
|
2411
|
+
out.hint = '무료 티어: 60 req/min, 1000 req/day · 유료는 https://ai.google.dev/gemini-api/docs/rate-limits';
|
|
2412
|
+
} else if (agent.id === 'copilot') {
|
|
2413
|
+
// gh copilot은 GitHub Copilot 구독 (월 단위 quota 없음, individual/business 플랜)
|
|
2414
|
+
const r = cp.spawnSync('gh', ['auth', 'status'], { encoding: 'utf8', timeout: 4000, shell: true });
|
|
2415
|
+
const authed = r.status === 0;
|
|
2416
|
+
out.quota = authed ? 'subscription' : 'not-authed';
|
|
2417
|
+
out.hint = authed ? 'Copilot 구독자 무제한 (월 플랜) · https://github.com/settings/copilot' : '`gh auth login` 필요';
|
|
2418
|
+
}
|
|
2419
|
+
} catch (e) {
|
|
2420
|
+
out.quota = 'error';
|
|
2421
|
+
out.hint = e.message;
|
|
2422
|
+
}
|
|
2423
|
+
results.push(out);
|
|
2424
|
+
}
|
|
2425
|
+
if (has('--json')) { log(JSON.stringify({ quota: results }, null, 2)); return; }
|
|
2426
|
+
log(`# 외부 AI CLI quota 추정 (1.9.31)`);
|
|
2427
|
+
log('');
|
|
2428
|
+
log(`| Agent | 상태 | quota | 안내 |`);
|
|
2429
|
+
log(`|---|---|---|---|`);
|
|
2430
|
+
for (const q of results) {
|
|
2431
|
+
const statusEmoji = q.status === 'ready' ? '🟢' : q.status === 'not-installed' ? '⚪' : q.status === 'disabled' ? '🟡' : '❓';
|
|
2432
|
+
log(`| ${q.id} | ${statusEmoji} ${q.status} | ${q.quota || '-'} | ${q.hint || '-'} |`);
|
|
2433
|
+
}
|
|
2434
|
+
log('');
|
|
2435
|
+
log(`## 주의`);
|
|
2436
|
+
log(` - leerness는 CLI 사용량을 직접 추적하지 않음 (provider 대시보드 참조)`);
|
|
2437
|
+
log(` - rate-limit/quota는 plan/티어에 따라 달라짐`);
|
|
2438
|
+
log(` - sub-agent 분배 시 quota 여유 큰 CLI 우선 활용 권장`);
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
fail('사용법: leerness agents list|check|quota|dispatch "<task>" --to <id>');
|
|
2443
|
+
return process.exit(1);
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2265
2446
|
function personaCmd(root, sub, idOrName, ...rest) {
|
|
2266
2447
|
root = absRoot(root || process.cwd());
|
|
2267
2448
|
if (!sub || sub === 'list') {
|
|
@@ -4452,7 +4633,7 @@ function viewworkInstall(root) {
|
|
|
4452
4633
|
}
|
|
4453
4634
|
|
|
4454
4635
|
function help() {
|
|
4455
|
-
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 워크스페이스 (--compact: LLM 시스템 프롬프트용 1줄 요약)\n leerness orchestrate "<목표>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL 필요)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벤치 히스토리 누적\n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on 역방향 추적 + 자동 회귀 sweep\n leerness memory search "키" [--include-code] # 1.9.25 소스 코드 본문도 검색 (모순 감지 핵심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 코드 본문 hits 포함\n leerness register-pending "<요청>" [--agent X] [--note Y] # 1.9.25 다중 세션 in-progress 즉시 등록\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 표시 감지 (1.9.27: 10 카테고리 + URL/메서드 매핑 + 신뢰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페르소나 카탈로그 (보안/성능/UX/testing/docs 5종 내장)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페르소나 리뷰 프롬프트 자동 생성\n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 표시 자동 검사 통합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 중복/잠재중복/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence 자동 검증 (1.9.20: scenes/scripts 등 도메인 폴더 + jest/mocha 파싱)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench 추가 실행 + evidence 누적\n leerness session close [path]\n leerness viewwork install [path]\n leerness viewwork emit [path] [--action a] [--note n] [--agent x] [--tool t]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
4636
|
+
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 워크스페이스 (--compact: LLM 시스템 프롬프트용 1줄 요약)\n leerness orchestrate "<목표>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL 필요)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벤치 히스토리 누적\n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on 역방향 추적 + 자동 회귀 sweep\n leerness memory search "키" [--include-code] # 1.9.25 소스 코드 본문도 검색 (모순 감지 핵심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 코드 본문 hits 포함\n leerness register-pending "<요청>" [--agent X] [--note Y] # 1.9.25 다중 세션 in-progress 즉시 등록\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 표시 감지 (1.9.27: 10 카테고리 + URL/메서드 매핑 + 신뢰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페르소나 카탈로그 (보안/성능/UX/testing/docs 5종 내장)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페르소나 리뷰 프롬프트 자동 생성\n leerness agents list|check # 1.9.30 외부 AI CLI 가용성 (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 활성 CLI 대상 실행 명령 생성 (실 호출 X, 사용자 실행)\n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 표시 자동 검사 통합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 중복/잠재중복/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence 자동 검증 (1.9.20: scenes/scripts 등 도메인 폴더 + jest/mocha 파싱)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench 추가 실행 + evidence 누적\n leerness session close [path]\n leerness viewwork install [path]\n leerness viewwork emit [path] [--action a] [--note n] [--agent x] [--tool t]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
4456
4637
|
leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # 회고 (1.9.13~1.9.16)
|
|
4457
4638
|
leerness insights [path] [--all-apps] [--include p1,p2] [--json] # 누적 통계 (1.9.13~1.9.16)
|
|
4458
4639
|
leerness brainstorm "<주제>" [--all-apps] [--include p1,p2] [--json] # 브레인스토밍 (1.9.13~1.9.16)
|
|
@@ -4495,6 +4676,7 @@ async function main() {
|
|
|
4495
4676
|
if (cmd === 'optimism-check') return optimismCheckCmd(arg('--path', process.cwd()), args[1]);
|
|
4496
4677
|
if (cmd === 'persona') return personaCmd(arg('--path', process.cwd()), args[1], args[2]);
|
|
4497
4678
|
if (cmd === 'review') return reviewCmd(arg('--path', process.cwd()), args[1]);
|
|
4679
|
+
if (cmd === 'agents') return agentsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
4498
4680
|
if (cmd === 'session' && args[1] === 'close') { const r = sessionClose(args[2] || process.cwd()); viewworkEmit(args[2] || process.cwd(), { action: 'task', tool: 'session-close', note: 'session close' }); return r; }
|
|
4499
4681
|
if (cmd === 'viewwork' && args[1] === 'install') return viewworkInstall(args[2] || process.cwd());
|
|
4500
4682
|
if (cmd === 'viewwork' && args[1] === 'emit') return viewworkEmit(args[2] || process.cwd(), { action: arg('--action','task'), note: arg('--note',''), agent: arg('--agent','leerness'), tool: arg('--tool','leerness-cli') });
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -794,6 +794,79 @@ total++;
|
|
|
794
794
|
if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
|
|
795
795
|
}
|
|
796
796
|
|
|
797
|
+
// 1.9.30 회귀: 외부 CLI 오케스트레이션 (agents list/check/dispatch)
|
|
798
|
+
total++;
|
|
799
|
+
{
|
|
800
|
+
// agents list — claude가 환경변수 + PATH 둘 다 충족 시 ready
|
|
801
|
+
const env1 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_GEMINI: '0', LEERNESS_ENABLE_COPILOT: '0' };
|
|
802
|
+
const r1 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list'], { encoding: 'utf8', timeout: 15000, env: env1 });
|
|
803
|
+
const okList = r1.status === 0
|
|
804
|
+
&& /외부 AI CLI 오케스트레이션 \(1\.9\.30\)/.test(r1.stdout)
|
|
805
|
+
&& /\| claude \|/.test(r1.stdout)
|
|
806
|
+
&& /\| codex \|/.test(r1.stdout)
|
|
807
|
+
&& /\| gemini \|/.test(r1.stdout)
|
|
808
|
+
&& /\| copilot \|/.test(r1.stdout);
|
|
809
|
+
// env 모두 0 → 비활성
|
|
810
|
+
const env2 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_GEMINI: '0', LEERNESS_ENABLE_COPILOT: '0' };
|
|
811
|
+
const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list', '--json'], { encoding: 'utf8', timeout: 15000, env: env2 });
|
|
812
|
+
let parsed = null;
|
|
813
|
+
try { parsed = JSON.parse(r2.stdout); } catch {}
|
|
814
|
+
const okJson = parsed && Array.isArray(parsed.agents) && parsed.agents.length === 4 && parsed.agents.every(a => a.status !== 'ready');
|
|
815
|
+
const ok = okList && okJson;
|
|
816
|
+
console.log(ok ? '✓ B(1.9.30) agents list: 4 CLI 정의 + env 0 시 모두 비활성' : `✗ agents list 실패 (list=${okList} json=${okJson})`);
|
|
817
|
+
if (!ok) { failed++; console.log(r1.stdout.slice(0, 500)); }
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
total++;
|
|
821
|
+
{
|
|
822
|
+
// agents dispatch — 활성 미충족 시 거부
|
|
823
|
+
const env = { ...process.env, LEERNESS_ENABLE_CODEX: '0' };
|
|
824
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', 'test task', '--to', 'codex'], { encoding: 'utf8', timeout: 10000, env });
|
|
825
|
+
const okBlocked = r.status !== 0 && /비활성|disabled|not-installed/i.test(r.stdout);
|
|
826
|
+
// --to 누락 거부
|
|
827
|
+
const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', 'test'], { encoding: 'utf8', timeout: 10000 });
|
|
828
|
+
const okNoTarget = r2.status !== 0 && /--to.*필요/.test(r2.stdout + r2.stderr);
|
|
829
|
+
// 알 수 없는 agent 거부
|
|
830
|
+
const r3 = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', 'test', '--to', 'jedi'], { encoding: 'utf8', timeout: 10000 });
|
|
831
|
+
const okBadAgent = r3.status !== 0 && /알 수 없는 agent/.test(r3.stdout + r3.stderr);
|
|
832
|
+
const ok = okBlocked && okNoTarget && okBadAgent;
|
|
833
|
+
console.log(ok ? '✓ B(1.9.30) agents dispatch: env=0/--to 누락/잘못된 agent 모두 거부' : `✗ dispatch 실패 (block=${okBlocked} noT=${okNoTarget} bad=${okBadAgent})`);
|
|
834
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 1.9.31 회귀: agents quota (각 CLI 사용량/quota 조회)
|
|
838
|
+
total++;
|
|
839
|
+
{
|
|
840
|
+
// agents quota — env=0 시 모두 disabled/not-installed, 안내 메시지 포함
|
|
841
|
+
const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_GEMINI: '0', LEERNESS_ENABLE_COPILOT: '0' };
|
|
842
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'quota'], { encoding: 'utf8', timeout: 15000, env });
|
|
843
|
+
const okText = r.status === 0
|
|
844
|
+
&& /외부 AI CLI quota 추정 \(1\.9\.31\)/.test(r.stdout)
|
|
845
|
+
&& /\| claude \|/.test(r.stdout)
|
|
846
|
+
&& /\| codex \|/.test(r.stdout)
|
|
847
|
+
&& /\| gemini \|/.test(r.stdout)
|
|
848
|
+
&& /\| copilot \|/.test(r.stdout)
|
|
849
|
+
&& /provider 대시보드 참조/.test(r.stdout);
|
|
850
|
+
// JSON 출력
|
|
851
|
+
const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'quota', '--json'], { encoding: 'utf8', timeout: 15000, env });
|
|
852
|
+
let parsed = null;
|
|
853
|
+
try { parsed = JSON.parse(r2.stdout); } catch {}
|
|
854
|
+
const okJson = parsed && Array.isArray(parsed.quota) && parsed.quota.length === 4
|
|
855
|
+
&& parsed.quota.every(q => typeof q.id === 'string' && typeof q.status === 'string' && (q.hint === null || typeof q.hint === 'string'));
|
|
856
|
+
const ok = okText && okJson;
|
|
857
|
+
console.log(ok ? '✓ B(1.9.31) agents quota: 4 CLI 사용량/안내 + JSON 출력' : `✗ quota 실패 (text=${okText} json=${okJson})`);
|
|
858
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
total++;
|
|
862
|
+
{
|
|
863
|
+
// 사용법 메시지에 quota 포함
|
|
864
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'foo'], { encoding: 'utf8', timeout: 10000 });
|
|
865
|
+
const ok = r.status !== 0 && /list\|check\|quota\|dispatch/.test(r.stdout + r.stderr);
|
|
866
|
+
console.log(ok ? '✓ B(1.9.31) agents 사용법에 quota 명시' : `✗ usage 메시지 실패`);
|
|
867
|
+
if (!ok) { failed++; console.log((r.stdout + r.stderr).slice(0, 300)); }
|
|
868
|
+
}
|
|
869
|
+
|
|
797
870
|
// 1.9.22 회귀: handoff --compact + orchestrate opt-in 정책 + llm-bench record
|
|
798
871
|
total++;
|
|
799
872
|
{
|