leerness 1.9.290 → 1.9.292
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 +35 -0
- package/README.md +6 -6
- package/bin/harness.js +82 -142
- package/lib/agent-registry.js +147 -0
- package/package.json +1 -1
- package/scripts/e2e.js +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.292 — 2026-06-03 — UR-0031: get_project_context — MCP 시맨틱 verb (에이전트 온보딩 1콜 집약)
|
|
4
|
+
|
|
5
|
+
**🧭 GPT-5.5 "MCP-first 범용 하네스" 전략의 핵심 — 어떤 에이전트든 leerness 내부 명령을 몰라도 단 1콜로 "지금 무엇을 알아야 하는가"를 파악하는 집약 컨텍스트 verb 신설.**
|
|
6
|
+
|
|
7
|
+
### 배경
|
|
8
|
+
1.9.279에서 `leerness_state_*` 도구 설명문에 시맨틱 verb(get_project_context 등)를 표기했으나, 실제 호출 가능한 이름은 여전히 `leerness_state_*` → MCP 클라이언트가 `tools/list`에서 의도 기반 이름을 못 봄. 또한 에이전트가 작업 시작 전 컨텍스트를 모으려면 handoff/state/task/decision/rule 여러 도구를 따로 호출해야 했음.
|
|
9
|
+
|
|
10
|
+
### 구현 (UR-0031)
|
|
11
|
+
1. **`leerness context [path] [--json]` CLI 신설** — 현재 작업(in-progress task) / 미답 사용자 요청 / 최근 결정 3건 / 활성 룰 / next-actions / memory surface(진행·결정·룰·교훈) / 프로젝트 의도(project-brief Purpose)를 **단일 구조화 객체**로 집약. 기존 헬퍼(readProgressRows·readRules·_loadUserRequests·_extractDecisionBlocks·_loadNextActionQueue) 재사용 — 신규 가치(집약)이지 단일 도구 중복 아님.
|
|
12
|
+
2. **MCP `leerness_get_project_context` (80번째 도구)** — `context --json` 호출. read-only. handoff(인간용 장문)와 달리 기계 친화 lean payload. 응답: `{ version, project, currentTask, openRequests, recentDecisions, activeRules, nextActions, memory }`.
|
|
13
|
+
3. selftest 39→40 (MCP verb 등록 + CLI 디스패치 + `_mcpToolCount()≥80` 소스 정합) · e2e 236→237 (init된 프로젝트에서 집약 JSON 구조 + MCP 80 도구 실측). README MCP 배지 79→80 자동 동기화.
|
|
14
|
+
|
|
15
|
+
### 검증
|
|
16
|
+
- **selftest 40/40 PASS** · **E2E 237/237 PASS** (회귀 0).
|
|
17
|
+
- B(1.9.288) 도구수 정합 테스트가 배지-live 불일치(79 vs 80)를 즉시 검출 → readme sync로 80 정합(Codex #5 가드 정상 동작 확인).
|
|
18
|
+
- 실측: `context --json` → openRequests 3건(전략 백로그) + activeRules R-0001 + memory 집약. MCP tools/list 80개에 `leerness_get_project_context` 노출.
|
|
19
|
+
|
|
20
|
+
## 1.9.291 — 2026-06-03 — UR-0025 (2단계): 외부 에이전트 레지스트리 → lib/agent-registry.js 모듈 분리
|
|
21
|
+
|
|
22
|
+
**🧩 GPT-5.5 + Codex 두 리뷰가 공통 지적한 #1 유지보수 이슈(단일 1.2MB 파일)를 비파괴 모듈 분리로 점진 해소. 1.9.274의 lib/pure-utils.js 패턴 계승.**
|
|
23
|
+
|
|
24
|
+
### 배경
|
|
25
|
+
`bin/harness.js` 단일 대형 파일은 외부 리뷰 2건(GPT-5.5·Codex)이 모두 지적한 유지보수/리뷰가능성 이슈. 1.9.274(UR-0025 1단계)에서 순수 함수 14종을 `lib/pure-utils.js`로 분리했고, 이번 2단계는 **순수 데이터 카탈로그**를 별도 모듈로 분리.
|
|
26
|
+
|
|
27
|
+
### 구현 (UR-0025 2단계)
|
|
28
|
+
1. **`lib/agent-registry.js` 신설** — `EXTERNAL_AGENTS`(10종 CLI: claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama) + `AGENT_SLASH_COMMANDS`(9종 슬래시/하위명령 레지스트리) 데이터 이동(140줄).
|
|
29
|
+
2. **비파괴 require-based 분리** — harness.js 는 `require('../lib/agent-registry')` 구조분해로 동일 바인딩 사용. 런타임 변형 0 검증(사용자 override 는 `_loadAgentSlashCommands` 가 별도 객체에 병합, base 불변).
|
|
30
|
+
3. **harness.js 21663→21525줄** (138줄 감소). `package.json` files 에 이미 `lib` 포함 → npm 배포 자동 반영.
|
|
31
|
+
4. selftest 38→39 (`m.EXTERNAL_AGENTS === EXTERNAL_AGENTS` 단일출처 동일참조 + 인라인 정의 제거 검증) · e2e 235→236 (모듈 standalone require + harness 단일출처 + 인라인 제거 실측).
|
|
32
|
+
|
|
33
|
+
### 검증
|
|
34
|
+
- **selftest 39/39 PASS** · **E2E 236/236 PASS** (회귀 0).
|
|
35
|
+
- 기존 카탈로그 selftest(EXTERNAL_AGENTS 10종 / AGENT_SLASH_COMMANDS 5종 / _agentSlashHint)가 추출 정합성 즉시 검증.
|
|
36
|
+
- 남은 UR-0025 후속: ROLE_CATALOG/CAPABILITY_SURFACE/ADAPTERS/REUSE 등 잔여 카탈로그 점진 분리(다음 라운드 후보).
|
|
37
|
+
|
|
3
38
|
## 1.9.290 — 2026-06-03 — UR-0037 (Codex #4): require 시 top-level side effect 격리 — Codex 5건 완전 수렴 🎉
|
|
4
39
|
|
|
5
40
|
**🧩 `require('leerness/bin/harness.js')` 가 호스트 프로세스를 오염시키던 마지막 갭 수정 (Codex gpt-5.5 #4). 이로써 Codex 코드 리뷰 5건 전부 수렴 완료.**
|
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
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -471,7 +471,7 @@ MIT — © leerness contributors
|
|
|
471
471
|
<!-- leerness:project-readme:start -->
|
|
472
472
|
## Leerness Project Harness
|
|
473
473
|
|
|
474
|
-
이 프로젝트는 Leerness v1.9.
|
|
474
|
+
이 프로젝트는 Leerness v1.9.292 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
475
475
|
|
|
476
476
|
### Core Commands
|
|
477
477
|
|
|
@@ -513,7 +513,7 @@ leerness memory restore decision <date|title>
|
|
|
513
513
|
|
|
514
514
|
### MCP server (외부 AI 통합)
|
|
515
515
|
|
|
516
|
-
Leerness v1.9.
|
|
516
|
+
Leerness v1.9.292는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **80개 도구**를 노출:
|
|
517
517
|
|
|
518
518
|
```jsonc
|
|
519
519
|
// 카테고리별
|
|
@@ -526,7 +526,7 @@ Leerness v1.9.290는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
526
526
|
// • Workflow: session_close / agents_list / task_export / env_check / usage_stats / reuse_map / whats_new
|
|
527
527
|
|
|
528
528
|
// MCP server 실행: leerness mcp serve
|
|
529
|
-
// tools/list 응답:
|
|
529
|
+
// tools/list 응답: 80 도구
|
|
530
530
|
```
|
|
531
531
|
|
|
532
532
|
### Autonomous mode (자율 모드)
|
|
@@ -534,7 +534,7 @@ Leerness v1.9.290는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
534
534
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
535
535
|
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) 다음 라운드 예약.
|
|
536
536
|
|
|
537
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
537
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.292)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
538
538
|
|
|
539
539
|
### 성능 가이드 (1.9.140 측정)
|
|
540
540
|
|
|
@@ -572,6 +572,6 @@ leerness release pack --close --auto-main-push
|
|
|
572
572
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
573
573
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
574
574
|
|
|
575
|
-
Last synced by Leerness v1.9.
|
|
575
|
+
Last synced by Leerness v1.9.292: 2026-06-03
|
|
576
576
|
<!-- leerness:project-readme:end -->
|
|
577
577
|
|
package/bin/harness.js
CHANGED
|
@@ -10,7 +10,7 @@ const readline = require('readline');
|
|
|
10
10
|
const { _isSecretKey, compareVer, parseHarnessVersion, _classifyCJK, _riskLabel, _detectSystemLang, _parseSlashFromHelp,
|
|
11
11
|
PERMISSION_TIERS, _tierRank, _requiredTier, _policyAllows, _resolveNpmTag, _mcpJsonContent, _newRunRecord } = require('../lib/pure-utils');
|
|
12
12
|
|
|
13
|
-
const VERSION = '1.9.
|
|
13
|
+
const VERSION = '1.9.292';
|
|
14
14
|
|
|
15
15
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
16
16
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -2997,6 +2997,8 @@ function _selfTestCases() {
|
|
|
2997
2997
|
{ name: '_newRunRecord: GPT-5.5 권고 스키마 14필드 + 기본값 (1.9.278)', run: () => { const r = _newRunRecord({ run_id: 'run-0001', goal: 'g', started_at: '2026-06-03T00:00:00Z' }); return r.schemaVersion === 1 && r.run_id === 'run-0001' && r.started_at === '2026-06-03T00:00:00Z' && Array.isArray(r.files_changed) && Array.isArray(r.commands_run) && Array.isArray(r.tests_run) && Array.isArray(r.decisions) && r.verification_result === null && r.status === 'in-progress' && 'handoff_summary' in r && 'model_name' in r && 'task_id' in r; } },
|
|
2998
2998
|
{ name: 'coreFiles --minimal: 핵심 유지 + 비핵심 제외 + verify 필수 보존 (1.9.276)', run: () => { const full = coreFiles('.', 'ko', []); const min = coreFiles('.', 'ko', [], { minimal: true }); const keep = ['.harness/plan.md','.harness/progress-tracker.md','.harness/session-handoff.md','AGENTS.md','CLAUDE.md','.harness/consistency-policy.md','.harness/reuse-map.md','.harness/encoding-policy.md','.harness/secret-policy.md']; const drop = ['.cursor/rules/leerness.mdc','.harness/skill-index.md','.harness/architecture.md']; const verifyReq = ['.harness/design-system.md','.harness/protected-files.md','.harness/current-state.md']; if (!verifyReq.every(k => min[k])) return false; return Object.keys(min).length < Object.keys(full).length && keep.every(k => min[k]) && drop.every(k => !min[k]); } },
|
|
2999
2999
|
{ name: '_cliBootstrap: CLI 부작용 require.main 가드 격리 (Codex #4 UR-0037 1.9.290)', run: () => { if (typeof _cliBootstrap !== 'function' || typeof _ensureStdoutEncoding !== 'function') return false; const src = read(__filename); const guarded = /if \(require\.main === module\) _cliBootstrap\(\);/.test(src); const inFn = /function _cliBootstrap\(\)\s*\{[\s\S]*?removeAllListeners\('warning'\)[\s\S]*?NODE_OPTIONS[\s\S]*?_ensureStdoutEncoding\(\);[\s\S]*?\n\}/.test(src); const noTopIife = !/\}\)\(\);\s*\n\n\/\/ 1\.9\.184/.test(src); return guarded && inFn && noTopIife; } },
|
|
3000
|
+
{ name: 'lib/agent-registry: EXTERNAL_AGENTS/AGENT_SLASH_COMMANDS 모듈 단일출처 분리 (UR-0025 1.9.291)', run: () => { const m = require('../lib/agent-registry'); return m.EXTERNAL_AGENTS === EXTERNAL_AGENTS && m.AGENT_SLASH_COMMANDS === AGENT_SLASH_COMMANDS && m.EXTERNAL_AGENTS.length === 10 && Object.keys(m.AGENT_SLASH_COMMANDS).length === 9 && !/const EXTERNAL_AGENTS = \[/.test(read(__filename)); } },
|
|
3001
|
+
{ name: 'get_project_context: MCP 시맨틱 verb 등록 + CLI context 디스패치 (UR-0031 1.9.292)', run: () => { const src = read(__filename); const mcpDef = /name: 'leerness_get_project_context'/.test(src); const mcpCase = /case 'leerness_get_project_context':[\s\S]*?cliArgs = \['context'/.test(src); const cliDisp = /if \(cmd === 'context'\)\s+return contextCmd/.test(src); return typeof contextCmd === 'function' && mcpDef && mcpCase && cliDisp && _mcpToolCount() >= 80; } },
|
|
3000
3002
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3001
3003
|
];
|
|
3002
3004
|
}
|
|
@@ -10193,146 +10195,8 @@ function _resolvePersona(root, id) {
|
|
|
10193
10195
|
return null;
|
|
10194
10196
|
}
|
|
10195
10197
|
|
|
10196
|
-
// 1.9.
|
|
10197
|
-
|
|
10198
|
-
// 1.9.32: installCmd 추가 — setup-agents 시 자동 설치 시도 가능
|
|
10199
|
-
const EXTERNAL_AGENTS = [
|
|
10200
|
-
{ id: 'claude', bin: 'claude', envFlag: 'LEERNESS_ENABLE_CLAUDE', versionArgs: ['--version'], desc: 'Anthropic Claude Code CLI',
|
|
10201
|
-
installCmd: 'npm i -g @anthropic-ai/claude-code', installHint: 'https://docs.anthropic.com/en/docs/claude-code/setup' },
|
|
10202
|
-
{ id: 'codex', bin: 'codex', envFlag: 'LEERNESS_ENABLE_CODEX', versionArgs: ['--version'], desc: 'OpenAI Codex CLI (격리 sandbox)',
|
|
10203
|
-
installCmd: 'npm i -g @openai/codex', installHint: 'https://github.com/openai/codex' },
|
|
10204
|
-
// 1.9.248 (사용자 명시 UR-0017): Gemini CLI 제거 + Antigravity CLI (agy 명령어) 교체
|
|
10205
|
-
// Google Antigravity 는 IDE 기반 멀티 모달 에이전트 도구. agy CLI 는 워크스페이스 직접 수정 가능 모드 지원.
|
|
10206
|
-
// 기존 LEERNESS_ENABLE_AGY 환경변수는 LEERNESS_ENABLE_AGY 로 교체. provider id 도 'agy' 로 통일.
|
|
10207
|
-
{ id: 'agy', bin: 'agy', envFlag: 'LEERNESS_ENABLE_AGY', versionArgs: ['--version'], desc: 'Google Antigravity CLI (멀티모달 에이전트, --yolo 워크스페이스 수정 가능)',
|
|
10208
|
-
installCmd: 'npm i -g @google/antigravity-cli', installHint: 'https://antigravity.google.com (Antigravity IDE/CLI)' },
|
|
10209
|
-
// 1.9.268: grok 정식 provider 승격 (1.9.266 후속 task) — 기존 슬래시 레지스트리(grok)만 보유하던 상태에서 EXTERNAL_AGENTS 로 편입.
|
|
10210
|
-
// → provider cycle / setup-agents / dispatch / slash-commands --refresh probe 가 grok 도 자동 처리.
|
|
10211
|
-
{ id: 'grok', bin: 'grok', envFlag: 'LEERNESS_ENABLE_GROK', versionArgs: ['--version'], desc: 'xAI Grok CLI (커뮤니티 grok-cli, /model grok-beta 등)',
|
|
10212
|
-
installCmd: 'npm i -g @vibe-kit/grok-cli', installHint: 'https://github.com/superagent-ai/grok-cli (xAI API 키 필요 — /login 또는 GROK_API_KEY)' },
|
|
10213
|
-
// 1.9.277 (사용자 명시): 신규 CLI 에이전트 4종 — opencode/qwen/aider/goose. 활성 시 sub-agent dispatch/roles 라우팅 대상.
|
|
10214
|
-
{ id: 'opencode', bin: 'opencode', envFlag: 'LEERNESS_ENABLE_OPENCODE', versionArgs: ['--version'], desc: 'opencode — 오픈소스 터미널 AI 코딩 에이전트 (provider-agnostic)',
|
|
10215
|
-
installCmd: 'npm i -g opencode-ai', installHint: 'https://opencode.ai (또는 curl -fsSL https://opencode.ai/install | bash)' },
|
|
10216
|
-
{ id: 'qwen', bin: 'qwen', envFlag: 'LEERNESS_ENABLE_QWEN', versionArgs: ['--version'], desc: 'Qwen Code CLI (Alibaba qwen3-coder 등)',
|
|
10217
|
-
installCmd: 'npm i -g @qwen-code/qwen-code', installHint: 'https://github.com/QwenLM/qwen-code (DASHSCOPE/OpenAI 호환 키)' },
|
|
10218
|
-
{ id: 'aider', bin: 'aider', envFlag: 'LEERNESS_ENABLE_AIDER', versionArgs: ['--version'], desc: 'Aider — git-aware 페어 프로그래밍 CLI (--model 임의 지정)',
|
|
10219
|
-
installCmd: 'python -m pip install aider-install && aider-install', installHint: 'https://aider.chat (pip install aider-chat · API 키 env)' },
|
|
10220
|
-
{ id: 'goose', bin: 'goose', envFlag: 'LEERNESS_ENABLE_GOOSE', versionArgs: ['--version'], desc: 'Goose — Block 오픈소스 범용 로컬 AI 에이전트 (MCP 확장)',
|
|
10221
|
-
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 설정)' },
|
|
10222
|
-
{ id: 'copilot', bin: 'gh', envFlag: 'LEERNESS_ENABLE_COPILOT', versionArgs: ['copilot', '--version'], desc: 'GitHub Copilot CLI (gh copilot)',
|
|
10223
|
-
installCmd: 'gh extension install github/gh-copilot', installHint: 'https://github.com/github/gh-copilot (gh CLI 선행 설치 필요)' },
|
|
10224
|
-
// 1.9.146: Ollama 추가 (사용자 명시 요청 #3) — 로컬 LLM, HTTP API 11434
|
|
10225
|
-
{ id: 'ollama', bin: 'ollama', envFlag: 'LEERNESS_ENABLE_OLLAMA', versionArgs: ['--version'], desc: 'Ollama 로컬 LLM (http://localhost:11434, llama3/qwen 등)',
|
|
10226
|
-
installCmd: 'curl -fsSL https://ollama.com/install.sh | sh (또는 https://ollama.com/download)', installHint: 'ollama serve 실행 + ollama pull <model>' }
|
|
10227
|
-
];
|
|
10228
|
-
|
|
10229
|
-
// 1.9.265 (사용자 명시 UR-0021): CLI AI 에이전트별 슬래시 명령어 레지스트리.
|
|
10230
|
-
// 목적: 각 CLI (claude/codex/agy/grok/copilot) 의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 dispatch 시 알맞게 참조.
|
|
10231
|
-
// "항상 최신화" 3중 경로: (1) 빌트인은 leerness 릴리스마다 갱신(asOf), (2) 사용자 .harness/agent-slash-commands.json override 병합,
|
|
10232
|
-
// (3) [1.9.267 완료] `slash-commands --refresh` — 설치된 CLI 의 `--help` probe 로 자동 갱신(best-effort, 검출 0건이면 큐레이션 유지). 0-dep·offline-first.
|
|
10233
|
-
// 주의: 외부 CLI 의 슬래시 명령은 버전마다 변동 → asOf 표기 + 사용자 override 우선. type='subcommand' 는 슬래시가 아닌 하위명령(예: gh copilot).
|
|
10234
|
-
const AGENT_SLASH_COMMANDS = {
|
|
10235
|
-
claude: {
|
|
10236
|
-
label: 'Anthropic Claude Code', asOf: '1.9.265', invoke: 'slash',
|
|
10237
|
-
commands: [
|
|
10238
|
-
{ cmd: '/help', desc: '명령 목록' },
|
|
10239
|
-
{ cmd: '/clear', desc: '대화 컨텍스트 초기화' },
|
|
10240
|
-
{ cmd: '/compact', desc: '대화 압축 (컨텍스트 절약)' },
|
|
10241
|
-
{ cmd: '/init', desc: 'CLAUDE.md 생성/갱신' },
|
|
10242
|
-
{ cmd: '/model', desc: '모델 전환' },
|
|
10243
|
-
{ cmd: '/review', desc: 'PR/변경 리뷰' },
|
|
10244
|
-
{ cmd: '/agents', desc: '서브에이전트 관리' },
|
|
10245
|
-
{ cmd: '/mcp', desc: 'MCP 서버 상태/관리' },
|
|
10246
|
-
{ cmd: '/memory', desc: '메모리 파일 편집' },
|
|
10247
|
-
{ cmd: '/permissions', desc: '권한 설정' },
|
|
10248
|
-
{ cmd: '/cost', desc: '토큰 비용 표시' },
|
|
10249
|
-
{ cmd: '/resume', desc: '이전 세션 재개' },
|
|
10250
|
-
{ cmd: '/config', desc: '설정' }
|
|
10251
|
-
]
|
|
10252
|
-
},
|
|
10253
|
-
codex: {
|
|
10254
|
-
label: 'OpenAI Codex CLI', asOf: '1.9.265', invoke: 'slash',
|
|
10255
|
-
commands: [
|
|
10256
|
-
{ cmd: '/init', desc: '프로젝트 컨텍스트 초기화' },
|
|
10257
|
-
{ cmd: '/compact', desc: '대화 압축' },
|
|
10258
|
-
{ cmd: '/diff', desc: '작업 트리 diff 표시' },
|
|
10259
|
-
{ cmd: '/mention', desc: '파일 멘션/첨부' },
|
|
10260
|
-
{ cmd: '/status', desc: '세션 상태/사용량' },
|
|
10261
|
-
{ cmd: '/model', desc: '모델 전환' },
|
|
10262
|
-
{ cmd: '/approvals', desc: '승인 모드 변경 (sandbox)' },
|
|
10263
|
-
{ cmd: '/new', desc: '새 대화 시작' },
|
|
10264
|
-
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
10265
|
-
{ cmd: '/quit', desc: '종료' }
|
|
10266
|
-
]
|
|
10267
|
-
},
|
|
10268
|
-
agy: {
|
|
10269
|
-
label: 'Google Antigravity CLI', asOf: '1.9.265', invoke: 'slash',
|
|
10270
|
-
note: '큐레이션(best-effort) — agy CLI 는 신규 도구라 슬래시 명령이 변동 가능. --refresh(2단계) 또는 사용자 override 권장.',
|
|
10271
|
-
commands: [
|
|
10272
|
-
{ cmd: '/help', desc: '명령 목록' },
|
|
10273
|
-
{ cmd: '/init', desc: '워크스페이스 초기화' },
|
|
10274
|
-
{ cmd: '/model', desc: '모델 전환' },
|
|
10275
|
-
{ cmd: '/new', desc: '새 세션' },
|
|
10276
|
-
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
10277
|
-
{ cmd: '/status', desc: '상태 표시' }
|
|
10278
|
-
]
|
|
10279
|
-
},
|
|
10280
|
-
grok: {
|
|
10281
|
-
label: 'xAI Grok CLI', asOf: '1.9.268', invoke: 'slash',
|
|
10282
|
-
note: '1.9.268 정식 provider 승격 — slash-commands --refresh 로 자동 probe 가능. 커뮤니티 Grok CLI(@vibe-kit/grok-cli) 기준 큐레이션, 배포판마다 차이 가능.',
|
|
10283
|
-
commands: [
|
|
10284
|
-
{ cmd: '/help', desc: '명령 목록' },
|
|
10285
|
-
{ cmd: '/clear', desc: '대화 초기화' },
|
|
10286
|
-
{ cmd: '/model', desc: '모델 전환 (grok-beta 등)' },
|
|
10287
|
-
{ cmd: '/new', desc: '새 대화' },
|
|
10288
|
-
{ cmd: '/login', desc: 'xAI API 키 로그인' },
|
|
10289
|
-
{ cmd: '/exit', desc: '종료' }
|
|
10290
|
-
]
|
|
10291
|
-
},
|
|
10292
|
-
copilot: {
|
|
10293
|
-
label: 'GitHub Copilot CLI (gh copilot)', asOf: '1.9.265', invoke: 'subcommand',
|
|
10294
|
-
note: '슬래시가 아닌 gh 하위명령. 호출 형식: gh copilot <sub> "<질의>".',
|
|
10295
|
-
commands: [
|
|
10296
|
-
{ cmd: 'suggest', desc: '명령/코드 제안 (gh copilot suggest "...")' },
|
|
10297
|
-
{ cmd: 'explain', desc: '명령 설명 (gh copilot explain "...")' },
|
|
10298
|
-
{ cmd: 'config', desc: 'gh copilot 설정' }
|
|
10299
|
-
]
|
|
10300
|
-
},
|
|
10301
|
-
// 1.9.277: 신규 4종 슬래시/세션 명령 (best-effort 큐레이션 — 배포판마다 차이 가능, 사용자 override 권장)
|
|
10302
|
-
opencode: {
|
|
10303
|
-
label: 'opencode', asOf: '1.9.277', invoke: 'slash',
|
|
10304
|
-
note: 'TUI 세션 슬래시 — 배포판마다 차이 가능.',
|
|
10305
|
-
commands: [
|
|
10306
|
-
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/new', desc: '새 세션' }, { cmd: '/models', desc: '모델 선택' },
|
|
10307
|
-
{ cmd: '/init', desc: '프로젝트 컨텍스트' }, { cmd: '/undo', desc: '되돌리기' }, { cmd: '/share', desc: '세션 공유' }, { cmd: '/exit', desc: '종료' }
|
|
10308
|
-
]
|
|
10309
|
-
},
|
|
10310
|
-
qwen: {
|
|
10311
|
-
label: 'Qwen Code CLI', asOf: '1.9.277', invoke: 'slash',
|
|
10312
|
-
note: 'Gemini CLI 계열 슬래시(qwen-code) 기준 큐레이션.',
|
|
10313
|
-
commands: [
|
|
10314
|
-
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/clear', desc: '컨텍스트 초기화' }, { cmd: '/memory', desc: '메모리 관리' },
|
|
10315
|
-
{ cmd: '/tools', desc: '도구 목록' }, { cmd: '/model', desc: '모델 전환' }, { cmd: '/quit', desc: '종료' }
|
|
10316
|
-
]
|
|
10317
|
-
},
|
|
10318
|
-
aider: {
|
|
10319
|
-
label: 'Aider', asOf: '1.9.277', invoke: 'slash',
|
|
10320
|
-
note: 'aider 세션 내 슬래시 명령 (in-chat commands).',
|
|
10321
|
-
commands: [
|
|
10322
|
-
{ cmd: '/add', desc: '파일 컨텍스트 추가' }, { cmd: '/drop', desc: '파일 제거' }, { cmd: '/ask', desc: '코드 수정 없이 질문' },
|
|
10323
|
-
{ cmd: '/run', desc: '셸 명령 실행' }, { cmd: '/test', desc: '테스트 실행' }, { cmd: '/diff', desc: '변경 diff' },
|
|
10324
|
-
{ cmd: '/commit', desc: '커밋' }, { cmd: '/undo', desc: '마지막 커밋 취소' }, { cmd: '/help', desc: '도움말' }
|
|
10325
|
-
]
|
|
10326
|
-
},
|
|
10327
|
-
goose: {
|
|
10328
|
-
label: 'Goose (Block)', asOf: '1.9.277', invoke: 'slash',
|
|
10329
|
-
note: 'goose 세션 내 명령 (배포판마다 차이 가능).',
|
|
10330
|
-
commands: [
|
|
10331
|
-
{ cmd: '/?', desc: '도움말' }, { cmd: '/mode', desc: '모드 전환(auto/approve/chat)' }, { cmd: '/extension', desc: 'MCP 확장 관리' },
|
|
10332
|
-
{ cmd: '/builtin', desc: '빌트인 확장' }, { cmd: '/exit', desc: '종료' }
|
|
10333
|
-
]
|
|
10334
|
-
}
|
|
10335
|
-
};
|
|
10198
|
+
// 1.9.291 (UR-0025 2단계): 외부 에이전트 레지스트리(EXTERNAL_AGENTS + AGENT_SLASH_COMMANDS) 데이터 모듈 분리 (비파괴, require-based). selftest 3종 검증.
|
|
10199
|
+
const { EXTERNAL_AGENTS, AGENT_SLASH_COMMANDS } = require('../lib/agent-registry');
|
|
10336
10200
|
function _agentSlashFile(root) { return path.join(absRoot(root), '.harness', 'agent-slash-commands.json'); }
|
|
10337
10201
|
// 사용자 override 병합 — .harness/agent-slash-commands.json 에 동일 agent id 가 있으면 commands 를 교체(최신화).
|
|
10338
10202
|
// 파일 형식: { schemaVersion:1, agents: { <id>: { label?, asOf?, invoke?, note?, commands:[{cmd,desc}] } } }
|
|
@@ -16668,7 +16532,8 @@ function mcpServeCmd(root) {
|
|
|
16668
16532
|
{ name: 'leerness_state_start', description: '1.9.279 (UR-0031) — start_task. 새 작업 run 시작 (.leerness/runs/run-NNNN.json 생성). 인자: { path?, goal (required), agent?, model?, task? }. 응답: { started, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, goal: { type: 'string' }, agent: { type: 'string' }, model: { type: 'string' }, task: { type: 'string' } }, required: ['goal'] } },
|
|
16669
16533
|
{ name: 'leerness_state_record', description: '1.9.279 (UR-0031) — record_file_change / record_decision. 진행 중 run 에 변경 파일/명령/테스트/결정을 누적. 인자: { path?, filesChanged? (콤마), filesRead?, commands?, tests?, errors?, decision? }. 응답: { recorded, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, filesChanged: { type: 'string' }, filesRead: { type: 'string' }, commands: { type: 'string' }, tests: { type: 'string' }, errors: { type: 'string' }, decision: { type: 'string' } } } },
|
|
16670
16534
|
{ name: 'leerness_state_verify', description: '1.9.279 (UR-0031) — request_verification / verify_done. 현재 run 의 검증 결과 기록. 인자: { path?, result (required: "pass"|"fail"), note? }. 응답: { verified, result, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, result: { type: 'string', enum: ['pass', 'fail'] }, note: { type: 'string' } }, required: ['result'] } },
|
|
16671
|
-
{ name: 'leerness_state_handoff', description: '1.9.279 (UR-0031) — create_handoff / make_handoff. 현재 run 을 마감하고 다음 에이전트용 .leerness/handoff/latest.{md,json} 작성 (Claude→Goose→Codex 인수인계 표준). 인자: { path?, summary (required) }. 응답: { handoff, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, summary: { type: 'string' } }, required: ['summary'] } }
|
|
16535
|
+
{ name: 'leerness_state_handoff', description: '1.9.279 (UR-0031) — create_handoff / make_handoff. 현재 run 을 마감하고 다음 에이전트용 .leerness/handoff/latest.{md,json} 작성 (Claude→Goose→Codex 인수인계 표준). 인자: { path?, summary (required) }. 응답: { handoff, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, summary: { type: 'string' } }, required: ['summary'] } },
|
|
16536
|
+
{ name: 'leerness_get_project_context', description: '1.9.292 (UR-0031) — get_project_context. 외부 에이전트가 작업 시작 전 단 1콜로 "지금 무엇을 알아야 하는가"를 파악하는 집약 컨텍스트. 여러 소스(현재 작업/미답 사용자 요청/최근 결정/활성 룰/next-actions/memory surface/프로젝트 의도)를 한 번에 구조화 회수. read-only. 인자: { path? }. 응답: { version, project, currentTask, openRequests, recentDecisions, activeRules, nextActions, memory }.', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
|
|
16672
16537
|
];
|
|
16673
16538
|
|
|
16674
16539
|
function send(obj) {
|
|
@@ -16918,6 +16783,9 @@ function mcpServeCmd(root) {
|
|
|
16918
16783
|
case 'leerness_state_handoff':
|
|
16919
16784
|
cliArgs = ['state', 'handoff', String(args.summary || ''), '--path', targetPath, '--json'];
|
|
16920
16785
|
break;
|
|
16786
|
+
case 'leerness_get_project_context':
|
|
16787
|
+
cliArgs = ['context', '--path', targetPath, '--json'];
|
|
16788
|
+
break;
|
|
16921
16789
|
default:
|
|
16922
16790
|
return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
|
|
16923
16791
|
}
|
|
@@ -18242,6 +18110,77 @@ function _saveRun(root, rec) { const f = _runFile(root, rec.run_id); mkdirp(path
|
|
|
18242
18110
|
function _splitList(v) { return String(v || '').split(',').map(s => s.trim()).filter(Boolean); }
|
|
18243
18111
|
|
|
18244
18112
|
// leerness state <show|start|record|verify|handoff>
|
|
18113
|
+
// 1.9.292 (UR-0031): get_project_context — 외부 에이전트 온보딩용 단일 집약 컨텍스트.
|
|
18114
|
+
// 1콜로 현재 작업/미답 요청/최근 결정/활성 룰/next-actions/memory/프로젝트 의도를 구조화 회수.
|
|
18115
|
+
// MCP leerness_get_project_context 가 이 명령(--json)을 호출. handoff(인간용 장문)와 달리 기계 친화 lean payload.
|
|
18116
|
+
function contextCmd(root, opts = {}) {
|
|
18117
|
+
root = absRoot(root);
|
|
18118
|
+
const rows = readProgressRows(root);
|
|
18119
|
+
const active = rows.find(r => r.status === 'in-progress' || r.status === '[진행]');
|
|
18120
|
+
const activeRules = readRules(root).filter(r => r.status === 'active');
|
|
18121
|
+
const openReqs = (_loadUserRequests(root).requests || []).filter(r => r.status === 'open' || r.status === 'in-progress');
|
|
18122
|
+
// 최근 결정 (last 3, 최신순) — decisionListCmd 와 동일 파서 재사용
|
|
18123
|
+
const decFile = decisionsPath(root);
|
|
18124
|
+
let recentDecisions = [];
|
|
18125
|
+
let decisionCount = 0;
|
|
18126
|
+
if (exists(decFile)) {
|
|
18127
|
+
const dtext = read(decFile);
|
|
18128
|
+
decisionCount = (dtext.match(/^### \d{4}-\d{2}-\d{2}/gm) || []).length;
|
|
18129
|
+
const blocks = _extractDecisionBlocks(dtext);
|
|
18130
|
+
recentDecisions = blocks.slice(-3).reverse().map(block => {
|
|
18131
|
+
const tm = (block.match(/^### (.+)$/m) || [])[1] || '';
|
|
18132
|
+
const dt = tm.match(/^(\d{4}-\d{2}-\d{2})\s*—\s*(.+)$/);
|
|
18133
|
+
return { date: dt ? dt[1] : null, title: (dt ? dt[2] : tm).trim().slice(0, 100) };
|
|
18134
|
+
});
|
|
18135
|
+
}
|
|
18136
|
+
const queueState = _loadNextActionQueue(root);
|
|
18137
|
+
const nextActions = (queueState.queue || []).slice(-3).reverse().map(a => ({ title: a.title, command: a.command || null }));
|
|
18138
|
+
const memory = {
|
|
18139
|
+
tasksInProgress: rows.filter(r => r.status === 'in-progress').length,
|
|
18140
|
+
decisions: decisionCount,
|
|
18141
|
+
rulesActive: activeRules.length,
|
|
18142
|
+
lessons: exists(lessonsPath(root)) ? (read(lessonsPath(root)).match(/^### \d{4}-\d{2}-\d{2}/gm) || []).length : 0
|
|
18143
|
+
};
|
|
18144
|
+
// 프로젝트 의도 (best-effort) — .harness/project-brief.md 의 ## Purpose 첫 줄
|
|
18145
|
+
let intent = null;
|
|
18146
|
+
const briefPath = path.join(root, '.harness', 'project-brief.md');
|
|
18147
|
+
if (exists(briefPath)) {
|
|
18148
|
+
const m = read(briefPath).match(/##\s*Purpose\s*\n+\s*[-*]?\s*([^\n#].*)/);
|
|
18149
|
+
if (m && !/업데이트하세요/.test(m[1])) intent = m[1].trim().slice(0, 200);
|
|
18150
|
+
}
|
|
18151
|
+
const ctx = {
|
|
18152
|
+
schemaVersion: 1,
|
|
18153
|
+
version: VERSION,
|
|
18154
|
+
project: { root, intent },
|
|
18155
|
+
currentTask: active ? { id: active.id, request: (active.request || '').slice(0, 200), status: active.status, nextAction: (active.nextAction || '').slice(0, 160) || null } : null,
|
|
18156
|
+
openRequests: { count: openReqs.length, items: openReqs.slice(0, 5).map(r => ({ id: r.id, text: (r.text || '').slice(0, 120) })) },
|
|
18157
|
+
recentDecisions,
|
|
18158
|
+
activeRules: activeRules.map(r => ({ id: r.id, trigger: r.trigger, rule: r.rule })),
|
|
18159
|
+
nextActions,
|
|
18160
|
+
memory
|
|
18161
|
+
};
|
|
18162
|
+
if (opts.json || has('--json')) { process.stdout.write(JSON.stringify(ctx, null, 2) + '\n'); return ctx; }
|
|
18163
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
18164
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
18165
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
18166
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
18167
|
+
log(cy(`# leerness context (1.9.292) — 에이전트 온보딩 컨텍스트 (v${VERSION})`));
|
|
18168
|
+
if (intent) log(` 🎯 의도: ${intent}`);
|
|
18169
|
+
log('');
|
|
18170
|
+
if (ctx.currentTask) {
|
|
18171
|
+
log(gr(`▶ 현재 작업: ${ctx.currentTask.id} — ${ctx.currentTask.request}`));
|
|
18172
|
+
if (ctx.currentTask.nextAction) log(dm(` 다음: ${ctx.currentTask.nextAction}`));
|
|
18173
|
+
} else log(dm('▶ 현재 진행 중 작업 없음'));
|
|
18174
|
+
log('');
|
|
18175
|
+
log(`📥 미답 요청: ${ctx.openRequests.count}건`);
|
|
18176
|
+
ctx.openRequests.items.forEach(r => log(dm(` • [${r.id}] ${r.text}`)));
|
|
18177
|
+
log('');
|
|
18178
|
+
log(`🧠 메모리: 진행 ${memory.tasksInProgress} / 결정 ${memory.decisions} / 룰 ${memory.rulesActive} / 교훈 ${memory.lessons}`);
|
|
18179
|
+
if (recentDecisions.length) { log(''); log('🗂 최근 결정:'); recentDecisions.forEach(d => log(dm(` • ${d.date || '?'} — ${d.title}`))); }
|
|
18180
|
+
if (ctx.activeRules.length) { log(''); log('⚡ 활성 룰:'); ctx.activeRules.forEach(r => log(dm(` • [${r.id}] (${r.trigger}) ${r.rule}`))); }
|
|
18181
|
+
if (nextActions.length) { log(''); log('👉 다음 액션:'); nextActions.forEach(a => log(dm(` • ${a.title}${a.command ? ' → ' + a.command : ''}`))); }
|
|
18182
|
+
return ctx;
|
|
18183
|
+
}
|
|
18245
18184
|
function stateCmd(root, sub, ...args) {
|
|
18246
18185
|
root = absRoot(root || process.cwd());
|
|
18247
18186
|
const json = has('--json');
|
|
@@ -21452,6 +21391,7 @@ async function main() {
|
|
|
21452
21391
|
if (cmd === 'capabilities' || cmd === 'security-surface') return capabilitiesCmd(arg('--path', process.cwd()), { json: has('--json') });
|
|
21453
21392
|
// 1.9.278 (UR-0032): leerness state <show|start|record|verify|handoff> — .leerness/ 구조화 상태 substrate
|
|
21454
21393
|
if (cmd === 'state') return stateCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
21394
|
+
if (cmd === 'context') return contextCmd(arg('--path', process.cwd()), { json: has('--json') });
|
|
21455
21395
|
// 1.9.281 (UR-0034): leerness policy <show|set|check> — 권한 등급 (opt-in enforced)
|
|
21456
21396
|
if (cmd === 'policy') return policyCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
21457
21397
|
// 1.9.285 (UR-0023): leerness reuse-check "<기능>" — 외부 OSS 빌드 vs 재사용 결정 게이트 (오프라인)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// lib/agent-registry.js — 외부 AI CLI 에이전트 레지스트리 (순수 데이터, 부작용 0)
|
|
2
|
+
// 1.9.291 (UR-0025 2단계): bin/harness.js 단일 대형 파일에서 비파괴 분리. selftest(EXTERNAL_AGENTS 10종 / AGENT_SLASH_COMMANDS 5종)가 동작 검증.
|
|
3
|
+
// 런타임 변형 없음 — 사용자 override 는 _loadAgentSlashCommands 가 별도 객체에 병합(base 불변).
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
// 1.9.30: 외부 AI CLI 오케스트레이션 — claude/codex/agy/copilot 가용성 + 활성화 체크
|
|
7
|
+
// 사용자 정책: 환경변수로 활성화 명시 + 실제 PATH 존재 확인 + 메인이 sub-agent 분배 시 참조
|
|
8
|
+
// 1.9.32: installCmd 추가 — setup-agents 시 자동 설치 시도 가능
|
|
9
|
+
const EXTERNAL_AGENTS = [
|
|
10
|
+
{ id: 'claude', bin: 'claude', envFlag: 'LEERNESS_ENABLE_CLAUDE', versionArgs: ['--version'], desc: 'Anthropic Claude Code CLI',
|
|
11
|
+
installCmd: 'npm i -g @anthropic-ai/claude-code', installHint: 'https://docs.anthropic.com/en/docs/claude-code/setup' },
|
|
12
|
+
{ id: 'codex', bin: 'codex', envFlag: 'LEERNESS_ENABLE_CODEX', versionArgs: ['--version'], desc: 'OpenAI Codex CLI (격리 sandbox)',
|
|
13
|
+
installCmd: 'npm i -g @openai/codex', installHint: 'https://github.com/openai/codex' },
|
|
14
|
+
// 1.9.248 (사용자 명시 UR-0017): Gemini CLI 제거 + Antigravity CLI (agy 명령어) 교체
|
|
15
|
+
// Google Antigravity 는 IDE 기반 멀티 모달 에이전트 도구. agy CLI 는 워크스페이스 직접 수정 가능 모드 지원.
|
|
16
|
+
// 기존 LEERNESS_ENABLE_AGY 환경변수는 LEERNESS_ENABLE_AGY 로 교체. provider id 도 'agy' 로 통일.
|
|
17
|
+
{ id: 'agy', bin: 'agy', envFlag: 'LEERNESS_ENABLE_AGY', versionArgs: ['--version'], desc: 'Google Antigravity CLI (멀티모달 에이전트, --yolo 워크스페이스 수정 가능)',
|
|
18
|
+
installCmd: 'npm i -g @google/antigravity-cli', installHint: 'https://antigravity.google.com (Antigravity IDE/CLI)' },
|
|
19
|
+
// 1.9.268: grok 정식 provider 승격 (1.9.266 후속 task) — 기존 슬래시 레지스트리(grok)만 보유하던 상태에서 EXTERNAL_AGENTS 로 편입.
|
|
20
|
+
// → provider cycle / setup-agents / dispatch / slash-commands --refresh probe 가 grok 도 자동 처리.
|
|
21
|
+
{ id: 'grok', bin: 'grok', envFlag: 'LEERNESS_ENABLE_GROK', versionArgs: ['--version'], desc: 'xAI Grok CLI (커뮤니티 grok-cli, /model grok-beta 등)',
|
|
22
|
+
installCmd: 'npm i -g @vibe-kit/grok-cli', installHint: 'https://github.com/superagent-ai/grok-cli (xAI API 키 필요 — /login 또는 GROK_API_KEY)' },
|
|
23
|
+
// 1.9.277 (사용자 명시): 신규 CLI 에이전트 4종 — opencode/qwen/aider/goose. 활성 시 sub-agent dispatch/roles 라우팅 대상.
|
|
24
|
+
{ id: 'opencode', bin: 'opencode', envFlag: 'LEERNESS_ENABLE_OPENCODE', versionArgs: ['--version'], desc: 'opencode — 오픈소스 터미널 AI 코딩 에이전트 (provider-agnostic)',
|
|
25
|
+
installCmd: 'npm i -g opencode-ai', installHint: 'https://opencode.ai (또는 curl -fsSL https://opencode.ai/install | bash)' },
|
|
26
|
+
{ id: 'qwen', bin: 'qwen', envFlag: 'LEERNESS_ENABLE_QWEN', versionArgs: ['--version'], desc: 'Qwen Code CLI (Alibaba qwen3-coder 등)',
|
|
27
|
+
installCmd: 'npm i -g @qwen-code/qwen-code', installHint: 'https://github.com/QwenLM/qwen-code (DASHSCOPE/OpenAI 호환 키)' },
|
|
28
|
+
{ id: 'aider', bin: 'aider', envFlag: 'LEERNESS_ENABLE_AIDER', versionArgs: ['--version'], desc: 'Aider — git-aware 페어 프로그래밍 CLI (--model 임의 지정)',
|
|
29
|
+
installCmd: 'python -m pip install aider-install && aider-install', installHint: 'https://aider.chat (pip install aider-chat · API 키 env)' },
|
|
30
|
+
{ id: 'goose', bin: 'goose', envFlag: 'LEERNESS_ENABLE_GOOSE', versionArgs: ['--version'], desc: 'Goose — Block 오픈소스 범용 로컬 AI 에이전트 (MCP 확장)',
|
|
31
|
+
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 설정)' },
|
|
32
|
+
{ id: 'copilot', bin: 'gh', envFlag: 'LEERNESS_ENABLE_COPILOT', versionArgs: ['copilot', '--version'], desc: 'GitHub Copilot CLI (gh copilot)',
|
|
33
|
+
installCmd: 'gh extension install github/gh-copilot', installHint: 'https://github.com/github/gh-copilot (gh CLI 선행 설치 필요)' },
|
|
34
|
+
// 1.9.146: Ollama 추가 (사용자 명시 요청 #3) — 로컬 LLM, HTTP API 11434
|
|
35
|
+
{ id: 'ollama', bin: 'ollama', envFlag: 'LEERNESS_ENABLE_OLLAMA', versionArgs: ['--version'], desc: 'Ollama 로컬 LLM (http://localhost:11434, llama3/qwen 등)',
|
|
36
|
+
installCmd: 'curl -fsSL https://ollama.com/install.sh | sh (또는 https://ollama.com/download)', installHint: 'ollama serve 실행 + ollama pull <model>' }
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
// 1.9.265 (사용자 명시 UR-0021): CLI AI 에이전트별 슬래시 명령어 레지스트리.
|
|
40
|
+
// 목적: 각 CLI (claude/codex/agy/grok/copilot) 의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 dispatch 시 알맞게 참조.
|
|
41
|
+
// "항상 최신화" 3중 경로: (1) 빌트인은 leerness 릴리스마다 갱신(asOf), (2) 사용자 .harness/agent-slash-commands.json override 병합,
|
|
42
|
+
// (3) [1.9.267 완료] `slash-commands --refresh` — 설치된 CLI 의 `--help` probe 로 자동 갱신(best-effort, 검출 0건이면 큐레이션 유지). 0-dep·offline-first.
|
|
43
|
+
// 주의: 외부 CLI 의 슬래시 명령은 버전마다 변동 → asOf 표기 + 사용자 override 우선. type='subcommand' 는 슬래시가 아닌 하위명령(예: gh copilot).
|
|
44
|
+
const AGENT_SLASH_COMMANDS = {
|
|
45
|
+
claude: {
|
|
46
|
+
label: 'Anthropic Claude Code', asOf: '1.9.265', invoke: 'slash',
|
|
47
|
+
commands: [
|
|
48
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
49
|
+
{ cmd: '/clear', desc: '대화 컨텍스트 초기화' },
|
|
50
|
+
{ cmd: '/compact', desc: '대화 압축 (컨텍스트 절약)' },
|
|
51
|
+
{ cmd: '/init', desc: 'CLAUDE.md 생성/갱신' },
|
|
52
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
53
|
+
{ cmd: '/review', desc: 'PR/변경 리뷰' },
|
|
54
|
+
{ cmd: '/agents', desc: '서브에이전트 관리' },
|
|
55
|
+
{ cmd: '/mcp', desc: 'MCP 서버 상태/관리' },
|
|
56
|
+
{ cmd: '/memory', desc: '메모리 파일 편집' },
|
|
57
|
+
{ cmd: '/permissions', desc: '권한 설정' },
|
|
58
|
+
{ cmd: '/cost', desc: '토큰 비용 표시' },
|
|
59
|
+
{ cmd: '/resume', desc: '이전 세션 재개' },
|
|
60
|
+
{ cmd: '/config', desc: '설정' }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
codex: {
|
|
64
|
+
label: 'OpenAI Codex CLI', asOf: '1.9.265', invoke: 'slash',
|
|
65
|
+
commands: [
|
|
66
|
+
{ cmd: '/init', desc: '프로젝트 컨텍스트 초기화' },
|
|
67
|
+
{ cmd: '/compact', desc: '대화 압축' },
|
|
68
|
+
{ cmd: '/diff', desc: '작업 트리 diff 표시' },
|
|
69
|
+
{ cmd: '/mention', desc: '파일 멘션/첨부' },
|
|
70
|
+
{ cmd: '/status', desc: '세션 상태/사용량' },
|
|
71
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
72
|
+
{ cmd: '/approvals', desc: '승인 모드 변경 (sandbox)' },
|
|
73
|
+
{ cmd: '/new', desc: '새 대화 시작' },
|
|
74
|
+
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
75
|
+
{ cmd: '/quit', desc: '종료' }
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
agy: {
|
|
79
|
+
label: 'Google Antigravity CLI', asOf: '1.9.265', invoke: 'slash',
|
|
80
|
+
note: '큐레이션(best-effort) — agy CLI 는 신규 도구라 슬래시 명령이 변동 가능. --refresh(2단계) 또는 사용자 override 권장.',
|
|
81
|
+
commands: [
|
|
82
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
83
|
+
{ cmd: '/init', desc: '워크스페이스 초기화' },
|
|
84
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
85
|
+
{ cmd: '/new', desc: '새 세션' },
|
|
86
|
+
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
87
|
+
{ cmd: '/status', desc: '상태 표시' }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
grok: {
|
|
91
|
+
label: 'xAI Grok CLI', asOf: '1.9.268', invoke: 'slash',
|
|
92
|
+
note: '1.9.268 정식 provider 승격 — slash-commands --refresh 로 자동 probe 가능. 커뮤니티 Grok CLI(@vibe-kit/grok-cli) 기준 큐레이션, 배포판마다 차이 가능.',
|
|
93
|
+
commands: [
|
|
94
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
95
|
+
{ cmd: '/clear', desc: '대화 초기화' },
|
|
96
|
+
{ cmd: '/model', desc: '모델 전환 (grok-beta 등)' },
|
|
97
|
+
{ cmd: '/new', desc: '새 대화' },
|
|
98
|
+
{ cmd: '/login', desc: 'xAI API 키 로그인' },
|
|
99
|
+
{ cmd: '/exit', desc: '종료' }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
copilot: {
|
|
103
|
+
label: 'GitHub Copilot CLI (gh copilot)', asOf: '1.9.265', invoke: 'subcommand',
|
|
104
|
+
note: '슬래시가 아닌 gh 하위명령. 호출 형식: gh copilot <sub> "<질의>".',
|
|
105
|
+
commands: [
|
|
106
|
+
{ cmd: 'suggest', desc: '명령/코드 제안 (gh copilot suggest "...")' },
|
|
107
|
+
{ cmd: 'explain', desc: '명령 설명 (gh copilot explain "...")' },
|
|
108
|
+
{ cmd: 'config', desc: 'gh copilot 설정' }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
// 1.9.277: 신규 4종 슬래시/세션 명령 (best-effort 큐레이션 — 배포판마다 차이 가능, 사용자 override 권장)
|
|
112
|
+
opencode: {
|
|
113
|
+
label: 'opencode', asOf: '1.9.277', invoke: 'slash',
|
|
114
|
+
note: 'TUI 세션 슬래시 — 배포판마다 차이 가능.',
|
|
115
|
+
commands: [
|
|
116
|
+
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/new', desc: '새 세션' }, { cmd: '/models', desc: '모델 선택' },
|
|
117
|
+
{ cmd: '/init', desc: '프로젝트 컨텍스트' }, { cmd: '/undo', desc: '되돌리기' }, { cmd: '/share', desc: '세션 공유' }, { cmd: '/exit', desc: '종료' }
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
qwen: {
|
|
121
|
+
label: 'Qwen Code CLI', asOf: '1.9.277', invoke: 'slash',
|
|
122
|
+
note: 'Gemini CLI 계열 슬래시(qwen-code) 기준 큐레이션.',
|
|
123
|
+
commands: [
|
|
124
|
+
{ cmd: '/help', desc: '명령 목록' }, { cmd: '/clear', desc: '컨텍스트 초기화' }, { cmd: '/memory', desc: '메모리 관리' },
|
|
125
|
+
{ cmd: '/tools', desc: '도구 목록' }, { cmd: '/model', desc: '모델 전환' }, { cmd: '/quit', desc: '종료' }
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
aider: {
|
|
129
|
+
label: 'Aider', asOf: '1.9.277', invoke: 'slash',
|
|
130
|
+
note: 'aider 세션 내 슬래시 명령 (in-chat commands).',
|
|
131
|
+
commands: [
|
|
132
|
+
{ cmd: '/add', desc: '파일 컨텍스트 추가' }, { cmd: '/drop', desc: '파일 제거' }, { cmd: '/ask', desc: '코드 수정 없이 질문' },
|
|
133
|
+
{ cmd: '/run', desc: '셸 명령 실행' }, { cmd: '/test', desc: '테스트 실행' }, { cmd: '/diff', desc: '변경 diff' },
|
|
134
|
+
{ cmd: '/commit', desc: '커밋' }, { cmd: '/undo', desc: '마지막 커밋 취소' }, { cmd: '/help', desc: '도움말' }
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
goose: {
|
|
138
|
+
label: 'Goose (Block)', asOf: '1.9.277', invoke: 'slash',
|
|
139
|
+
note: 'goose 세션 내 명령 (배포판마다 차이 가능).',
|
|
140
|
+
commands: [
|
|
141
|
+
{ cmd: '/?', desc: '도움말' }, { cmd: '/mode', desc: '모드 전환(auto/approve/chat)' }, { cmd: '/extension', desc: 'MCP 확장 관리' },
|
|
142
|
+
{ cmd: '/builtin', desc: '빌트인 확장' }, { cmd: '/exit', desc: '종료' }
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
module.exports = { EXTERNAL_AGENTS, AGENT_SLASH_COMMANDS };
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -3115,5 +3115,44 @@ total++;
|
|
|
3115
3115
|
if (!ok) failed++;
|
|
3116
3116
|
}
|
|
3117
3117
|
|
|
3118
|
+
// 1.9.291 회귀 (UR-0025 2단계): lib/agent-registry.js 모듈 분리 — 단일출처 + 부작용 0
|
|
3119
|
+
total++;
|
|
3120
|
+
{
|
|
3121
|
+
let ok = false;
|
|
3122
|
+
try {
|
|
3123
|
+
const reg = require(path.resolve(__dirname, '..', 'lib', 'agent-registry.js'));
|
|
3124
|
+
const h = require(path.resolve(__dirname, '..', 'bin', 'harness.js'));
|
|
3125
|
+
const dataOk = Array.isArray(reg.EXTERNAL_AGENTS) && reg.EXTERNAL_AGENTS.length === 10 &&
|
|
3126
|
+
reg.EXTERNAL_AGENTS.every(a => a.id && a.bin && a.envFlag) &&
|
|
3127
|
+
reg.AGENT_SLASH_COMMANDS && Object.keys(reg.AGENT_SLASH_COMMANDS).length === 9;
|
|
3128
|
+
// harness 가 모듈을 단일출처로 사용 (같은 객체 참조)
|
|
3129
|
+
const singleSource = h.AGENT_SLASH_COMMANDS === reg.AGENT_SLASH_COMMANDS;
|
|
3130
|
+
// harness.js 소스에 인라인 정의가 더 이상 없음 (모듈로 이동 완료)
|
|
3131
|
+
const harnessSrc = fs.readFileSync(path.resolve(__dirname, '..', 'bin', 'harness.js'), 'utf8');
|
|
3132
|
+
const movedOut = !/const EXTERNAL_AGENTS = \[/.test(harnessSrc) && /require\('\.\.\/lib\/agent-registry'\)/.test(harnessSrc);
|
|
3133
|
+
ok = dataOk && singleSource && movedOut;
|
|
3134
|
+
} catch {}
|
|
3135
|
+
console.log(ok ? '✓ B(1.9.291) lib/agent-registry 모듈 분리: 단일출처 + 인라인 제거 (UR-0025)' : '✗ agent-registry 모듈 분리 실패');
|
|
3136
|
+
if (!ok) failed++;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
// 1.9.292 회귀 (UR-0031): leerness context — get_project_context 집약 시맨틱 verb
|
|
3140
|
+
total++;
|
|
3141
|
+
{
|
|
3142
|
+
let ok = false;
|
|
3143
|
+
try {
|
|
3144
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'context', tmp, '--json'], { cwd: tmp, encoding: 'utf8', timeout: 30000 });
|
|
3145
|
+
const j = JSON.parse(r.stdout);
|
|
3146
|
+
const structOk = r.status === 0 && j.schemaVersion === 1 && !!j.version && !!j.project && ('currentTask' in j) &&
|
|
3147
|
+
j.openRequests && typeof j.openRequests.count === 'number' && Array.isArray(j.recentDecisions) &&
|
|
3148
|
+
Array.isArray(j.activeRules) && Array.isArray(j.nextActions) && j.memory && typeof j.memory.rulesActive === 'number';
|
|
3149
|
+
const h = require(path.resolve(__dirname, '..', 'bin', 'harness.js'));
|
|
3150
|
+
const mcpOk = h._mcpToolCount && h._mcpToolCount() >= 80;
|
|
3151
|
+
ok = structOk && mcpOk;
|
|
3152
|
+
} catch {}
|
|
3153
|
+
console.log(ok ? '✓ B(1.9.292) leerness context: get_project_context 집약 JSON 구조 + MCP 80 도구 (UR-0031)' : '✗ context 집약 verb 실패');
|
|
3154
|
+
if (!ok) failed++;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3118
3157
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
3119
3158
|
if (failed > 0) process.exit(1);
|