leerness 1.9.267 → 1.9.268

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.268 — 2026-06-02 — grok 정식 EXTERNAL_AGENTS provider 승격 (1.9.266 후속 task)
4
+
5
+ **🤖 grok 을 슬래시 레지스트리 전용에서 정식 provider 로 승격 — provider cycle / setup-agents / dispatch / `--help` probe 가 grok 도 자동 처리.**
6
+
7
+ ### 배경
8
+ 1.9.265~267(UR-0021)에서 grok 은 `AGENT_SLASH_COMMANDS` 슬래시 레지스트리에만 포함됐을 뿐 `EXTERNAL_AGENTS` 정식 provider 는 아니었음(1.9.266 CHANGELOG 명시 후속 task). 그 결과 `slash-commands --refresh` probe 대상에서 제외되고, install 흐름 6선택지·provider cycle·setup-agents 에서도 빠졌음. 1.9.267 의 `--help` probe(3단계)가 완성되면서 grok 을 정식 편입할 자연스러운 시점.
9
+
10
+ ### 구현
11
+ 1. **`EXTERNAL_AGENTS` 에 grok 추가** — `{ id:'grok', bin:'grok', envFlag:'LEERNESS_ENABLE_GROK', versionArgs:['--version'], installCmd:'npm i -g @vibe-kit/grok-cli' }`. 빌트인 5종 → **6종** (claude/codex/agy/**grok**/copilot/ollama).
12
+ 2. **자동 전파** — EXTERNAL_AGENTS 를 순회하는 모든 소비처가 grok 흡수: `_checkAgent`(ready 감지)·`provider list`(6 builtin)·`slash-commands --refresh`(probe 대상)·`setup-agents`·`agents dispatch`·`agents list/quota`.
13
+ 3. **install 흐름 6선택지 확장** — 대화형 `_selectMany` + 비대화형 `1~6` 맵에 grok 추가(`all` 배열 포함). `4) grok (xAI)` 신설로 5→6 선택지.
14
+ 4. **비시크릿 키 + config 템플릿** — `_LEERNESS_NONSECRET_KEYS` 및 `.harness/leerness-config.json` 템플릿에 `LEERNESS_ENABLE_GROK` 추가(AI 가시성 유지, 시크릿 아님).
15
+ 5. **슬래시 레지스트리 note 갱신** — grok `asOf: 1.9.268` + "정식 provider 승격 — --refresh 자동 probe 가능".
16
+ 6. **selftest 19 → 20** — grok EXTERNAL_AGENTS 편입(bin/envFlag/length===6) 검증.
17
+
18
+ ### 검증
19
+ - **selftest 20/20 PASS** · **E2E 217/217 PASS** (회귀 0).
20
+ - `provider list` → 총 6개(빌트인 6), grok=builtin 확인.
21
+ - `slash-commands grok --refresh --dry-run` → 이전 "probe 대상 아님" → 이제 probe 시도(미설치 시 ENOENT graceful fallback) — 3단계 probe 통합 확인.
22
+
3
23
  ## 1.9.267 — 2026-06-02 — UR-0021 3단계: CLI `--help` probe 슬래시 레지스트리 자동 refresh
4
24
 
5
25
  **🔄 UR-0021 백로그 완전 소진 — 설치된 CLI 의 `--help` 출력을 probe 해 슬래시 명령 레지스트리를 자동 갱신 (best-effort, offline-first).**
package/bin/harness.js CHANGED
@@ -7,7 +7,7 @@ const cp = require('child_process');
7
7
  const os = require('os'); // 1.9.178: _publishToNpm 에서 os.tmpdir() 사용 (전역 import)
8
8
  const readline = require('readline');
9
9
 
10
- const VERSION = '1.9.267';
10
+ const VERSION = '1.9.268';
11
11
 
12
12
  // 1.9.184: DEP0190 (child_process shell: true) deprecation warning 억제 (사용자 명시).
13
13
  // leerness 는 cross-platform PATH resolution 을 위해 shell: true 를 의도적으로 사용 (claude.cmd / ollama.cmd 등 Windows .cmd 처리).
@@ -829,19 +829,20 @@ async function resolveInstallOptions(root, opts = {}) {
829
829
  { label: 'Claude (ANTHROPIC_API_KEY 또는 claude CLI)', description: '추론력 최고 — 코드 작성/리뷰 기본', id: 'claude' },
830
830
  { label: 'Codex (OpenAI codex CLI)', description: 'OpenAI 코드 모델', id: 'codex' },
831
831
  { label: 'Antigravity (agy CLI)', description: 'Google Antigravity 멀티모달 에이전트 (1.9.248~)', id: 'agy' },
832
+ { label: 'Grok (xAI grok-cli)', description: 'xAI Grok CLI — grok-beta 등 (1.9.268~)', id: 'grok' },
832
833
  { label: 'Copilot (gh extension)', description: 'GitHub Copilot CLI', id: 'copilot' },
833
834
  { label: 'Ollama (로컬 LLM — llama3/qwen 등)', description: 'http://localhost:11434 — 무료/오프라인', id: 'ollama' }
834
835
  ], { defaults: [] });
835
836
  agentsOptIn = picked.length ? picked.map(p => p.id) : 'none';
836
837
  } else {
837
838
  log('\nCLI 에이전트 활성화 (복수 선택 — 콤마로 구분, opt-in):');
838
- log(' 1) claude 2) codex 3) agy (Antigravity) 4) copilot 5) ollama (예: 1,5 또는 all 또는 none)');
839
+ log(' 1) claude 2) codex 3) agy (Antigravity) 4) grok (xAI) 5) copilot 6) ollama (예: 1,6 또는 all 또는 none)');
839
840
  const a = (await ask('선택 [none]: ')).trim().toLowerCase();
840
- if (a === 'all') agentsOptIn = ['claude', 'codex', 'agy', 'copilot', 'ollama'];
841
+ if (a === 'all') agentsOptIn = ['claude', 'codex', 'agy', 'grok', 'copilot', 'ollama'];
841
842
  else if (!a || a === 'none' || a === '0') agentsOptIn = 'none';
842
843
  else {
843
- const map = { '1': 'claude', '2': 'codex', '3': 'agy', '4': 'copilot', '5': 'ollama' };
844
- const picks = a.split(/[,\s]+/).map(t => map[t] || (['claude','codex','agy','copilot','ollama'].includes(t) ? t : null)).filter(Boolean);
844
+ const map = { '1': 'claude', '2': 'codex', '3': 'agy', '4': 'grok', '5': 'copilot', '6': 'ollama' };
845
+ const picks = a.split(/[,\s]+/).map(t => map[t] || (['claude','codex','agy','grok','copilot','ollama'].includes(t) ? t : null)).filter(Boolean);
845
846
  agentsOptIn = picks.length ? picks : 'none';
846
847
  }
847
848
  }
@@ -1014,6 +1015,7 @@ async function install(root, opts = {}) {
1014
1015
  LEERNESS_ENABLE_CLAUDE: enable('claude') ? '1' : '0',
1015
1016
  LEERNESS_ENABLE_CODEX: enable('codex') ? '1' : '0',
1016
1017
  LEERNESS_ENABLE_AGY: enable('agy') ? '1' : '0',
1018
+ LEERNESS_ENABLE_GROK: enable('grok') ? '1' : '0',
1017
1019
  LEERNESS_ENABLE_COPILOT: enable('copilot') ? '1' : '0',
1018
1020
  LEERNESS_ENABLE_OLLAMA: enable('ollama') ? '1' : '0',
1019
1021
  LEERNESS_SKILL_DISCOVER_URL: '',
@@ -2909,6 +2911,7 @@ function _selfTestCases() {
2909
2911
  { 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); } },
2910
2912
  { 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)); } },
2911
2913
  { 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'); } },
2914
+ { name: 'EXTERNAL_AGENTS: grok 정식 provider 승격 (1.9.268)', run: () => { const g = EXTERNAL_AGENTS.find(a => a.id === 'grok'); return !!g && g.bin === 'grok' && g.envFlag === 'LEERNESS_ENABLE_GROK' && EXTERNAL_AGENTS.length === 6; } },
2912
2915
  { name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
2913
2916
  ];
2914
2917
  }
@@ -9417,6 +9420,7 @@ const _LEERNESS_NONSECRET_KEYS = new Set([
9417
9420
  'LEERNESS_ENABLE_CLAUDE', // 활성화 플래그
9418
9421
  'LEERNESS_ENABLE_CODEX',
9419
9422
  'LEERNESS_ENABLE_AGY',
9423
+ 'LEERNESS_ENABLE_GROK',
9420
9424
  'LEERNESS_ENABLE_COPILOT',
9421
9425
  'LEERNESS_ENABLE_OLLAMA',
9422
9426
  'LEERNESS_SKILL_DISCOVER_URL', // 공개 URL
@@ -10073,6 +10077,10 @@ const EXTERNAL_AGENTS = [
10073
10077
  // 기존 LEERNESS_ENABLE_AGY 환경변수는 LEERNESS_ENABLE_AGY 로 교체. provider id 도 'agy' 로 통일.
10074
10078
  { id: 'agy', bin: 'agy', envFlag: 'LEERNESS_ENABLE_AGY', versionArgs: ['--version'], desc: 'Google Antigravity CLI (멀티모달 에이전트, --yolo 워크스페이스 수정 가능)',
10075
10079
  installCmd: 'npm i -g @google/antigravity-cli', installHint: 'https://antigravity.google.com (Antigravity IDE/CLI)' },
10080
+ // 1.9.268: grok 정식 provider 승격 (1.9.266 후속 task) — 기존 슬래시 레지스트리(grok)만 보유하던 상태에서 EXTERNAL_AGENTS 로 편입.
10081
+ // → provider cycle / setup-agents / dispatch / slash-commands --refresh probe 가 grok 도 자동 처리.
10082
+ { id: 'grok', bin: 'grok', envFlag: 'LEERNESS_ENABLE_GROK', versionArgs: ['--version'], desc: 'xAI Grok CLI (커뮤니티 grok-cli, /model grok-beta 등)',
10083
+ installCmd: 'npm i -g @vibe-kit/grok-cli', installHint: 'https://github.com/superagent-ai/grok-cli (xAI API 키 필요 — /login 또는 GROK_API_KEY)' },
10076
10084
  { id: 'copilot', bin: 'gh', envFlag: 'LEERNESS_ENABLE_COPILOT', versionArgs: ['copilot', '--version'], desc: 'GitHub Copilot CLI (gh copilot)',
10077
10085
  installCmd: 'gh extension install github/gh-copilot', installHint: 'https://github.com/github/gh-copilot (gh CLI 선행 설치 필요)' },
10078
10086
  // 1.9.146: Ollama 추가 (사용자 명시 요청 #3) — 로컬 LLM, HTTP API 11434
@@ -10132,8 +10140,8 @@ const AGENT_SLASH_COMMANDS = {
10132
10140
  ]
10133
10141
  },
10134
10142
  grok: {
10135
- label: 'xAI Grok CLI', asOf: '1.9.265', invoke: 'slash',
10136
- note: '커뮤니티/공식 Grok CLI(예: @vibe-kit/grok-cli) 기준 큐레이션 배포판마다 차이 가능. 사용자 override 권장.',
10143
+ label: 'xAI Grok CLI', asOf: '1.9.268', invoke: 'slash',
10144
+ note: '1.9.268 정식 provider 승격 — slash-commands --refresh 로 자동 probe 가능. 커뮤니티 Grok CLI(@vibe-kit/grok-cli) 기준 큐레이션, 배포판마다 차이 가능.',
10137
10145
  commands: [
10138
10146
  { cmd: '/help', desc: '명령 목록' },
10139
10147
  { cmd: '/clear', desc: '대화 초기화' },
@@ -10281,7 +10289,7 @@ function _refreshAgentSlashCommands(root, targets, opts = {}) {
10281
10289
  for (const id of targets) {
10282
10290
  const ext = EXTERNAL_AGENTS.find(a => a.id === id);
10283
10291
  const def = merged[id] || AGENT_SLASH_COMMANDS[id];
10284
- if (!ext) { results.push({ id, ok: false, reason: 'probe 대상 아님 (EXTERNAL_AGENTS 미포함 — grok/ollama 등은 큐레이션/override )' }); continue; }
10292
+ if (!ext) { results.push({ id, ok: false, reason: 'probe 대상 아님 (EXTERNAL_AGENTS 미포함 — 사용자 override 전용 agent)' }); continue; }
10285
10293
  const pr = _probeAgentSlash(ext, { invoke: (def && def.invoke) || 'slash', timeout: opts.timeout });
10286
10294
  if (pr.ok) {
10287
10295
  agents[id] = {
@@ -10385,7 +10393,7 @@ function slashCommandsCmd(root, agentArg, opts = {}) {
10385
10393
  }
10386
10394
 
10387
10395
  // 1.9.157: Provider Registry — 사용자 정의 provider 동적 추가 (.harness/providers.json)
10388
- // 빌트인 5종 (EXTERNAL_AGENTS) + 사용자 정의를 merge. OpenRouter / Bedrock / Groq 등 새 CLI 즉시 흡수 가능.
10396
+ // 빌트인 6종 (EXTERNAL_AGENTS: claude/codex/agy/grok/copilot/ollama) + 사용자 정의를 merge. OpenRouter / Bedrock 등 새 CLI 즉시 흡수 가능.
10389
10397
  // 파일 형식: { "schemaVersion": 1, "providers": [{ id, bin, envFlag, versionArgs, desc, installHint }] }
10390
10398
  function _providersFile(root) { return path.join(absRoot(root), '.harness', 'providers.json'); }
10391
10399
  function _readUserProviders(root) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.267",
3
+ "version": "1.9.268",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -797,23 +797,24 @@ total++;
797
797
  total++;
798
798
  {
799
799
  // agents list — claude가 환경변수 + PATH 둘 다 충족 시 ready
800
- const env1 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_COPILOT: '0' };
800
+ const env1 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_GROK: '0', LEERNESS_ENABLE_COPILOT: '0' };
801
801
  const r1 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list'], { encoding: 'utf8', timeout: 15000, env: env1 });
802
802
  const okList = r1.status === 0
803
803
  && /외부 AI CLI 오케스트레이션 \(1\.9\.30\)/.test(r1.stdout)
804
804
  && /\| claude \|/.test(r1.stdout)
805
805
  && /\| codex \|/.test(r1.stdout)
806
806
  && /\| agy \|/.test(r1.stdout)
807
+ && /\| grok \|/.test(r1.stdout)
807
808
  && /\| copilot \|/.test(r1.stdout);
808
809
  // env 모두 0 → 비활성
809
- // 1.9.146: Ollama 추가 → 5 CLI
810
- const env2 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_COPILOT: '0', LEERNESS_ENABLE_OLLAMA: '0' };
810
+ // 1.9.146: Ollama 추가 → 5 CLI · 1.9.268: grok 정식 승격 → 6 CLI
811
+ const env2 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_GROK: '0', LEERNESS_ENABLE_COPILOT: '0', LEERNESS_ENABLE_OLLAMA: '0' };
811
812
  const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list', '--json'], { encoding: 'utf8', timeout: 15000, env: env2 });
812
813
  let parsed = null;
813
814
  try { parsed = JSON.parse(r2.stdout); } catch {}
814
- const okJson = parsed && Array.isArray(parsed.agents) && parsed.agents.length === 5 && parsed.agents.every(a => a.status !== 'ready');
815
+ const okJson = parsed && Array.isArray(parsed.agents) && parsed.agents.length === 6 && parsed.agents.every(a => a.status !== 'ready');
815
816
  const ok = okList && okJson;
816
- console.log(ok ? '✓ B(1.9.30+1.9.146) agents list: 5 CLI 정의 (claude/codex/agy/copilot/ollama)' : `✗ agents list 실패 (list=${okList} json=${okJson})`);
817
+ console.log(ok ? '✓ B(1.9.30+1.9.268) agents list: 6 CLI 정의 (claude/codex/agy/grok/copilot/ollama)' : `✗ agents list 실패 (list=${okList} json=${okJson})`);
817
818
  if (!ok) { failed++; console.log(r1.stdout.slice(0, 500)); }
818
819
  }
819
820
 
@@ -838,24 +839,25 @@ total++;
838
839
  total++;
839
840
  {
840
841
  // agents quota — env=0 시 모두 disabled/not-installed, 안내 메시지 포함
841
- const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_COPILOT: '0' };
842
+ const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_GROK: '0', LEERNESS_ENABLE_COPILOT: '0' };
842
843
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'quota'], { encoding: 'utf8', timeout: 15000, env });
843
844
  const okText = r.status === 0
844
845
  && /외부 AI CLI quota 추정 \(1\.9\.31\)/.test(r.stdout)
845
846
  && /\| claude \|/.test(r.stdout)
846
847
  && /\| codex \|/.test(r.stdout)
847
848
  && /\| agy \|/.test(r.stdout)
849
+ && /\| grok \|/.test(r.stdout)
848
850
  && /\| copilot \|/.test(r.stdout)
849
851
  && /provider 대시보드 참조/.test(r.stdout);
850
852
  // JSON 출력
851
853
  const r2 = cp.spawnSync(process.execPath, [CLI, 'agents', 'quota', '--json'], { encoding: 'utf8', timeout: 15000, env });
852
854
  let parsed = null;
853
855
  try { parsed = JSON.parse(r2.stdout); } catch {}
854
- // 1.9.146: Ollama 추가 → 5 CLI
855
- const okJson = parsed && Array.isArray(parsed.quota) && parsed.quota.length === 5
856
+ // 1.9.146: Ollama 추가 → 5 CLI · 1.9.268: grok 정식 승격 → 6 CLI
857
+ const okJson = parsed && Array.isArray(parsed.quota) && parsed.quota.length === 6
856
858
  && parsed.quota.every(q => typeof q.id === 'string' && typeof q.status === 'string' && (q.hint === null || typeof q.hint === 'string'));
857
859
  const ok = okText && okJson;
858
- console.log(ok ? '✓ B(1.9.31+1.9.146) agents quota: 5 CLI 사용량/안내' : `✗ quota 실패 (text=${okText} json=${okJson})`);
860
+ console.log(ok ? '✓ B(1.9.31+1.9.268) agents quota: 6 CLI 사용량/안내' : `✗ quota 실패 (text=${okText} json=${okJson})`);
859
861
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
860
862
  }
861
863