leerness 1.9.265 → 1.9.267
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 +42 -0
- package/README.md +3 -3
- package/bin/harness.js +167 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.267 — 2026-06-02 — UR-0021 3단계: CLI `--help` probe 슬래시 레지스트리 자동 refresh
|
|
4
|
+
|
|
5
|
+
**🔄 UR-0021 백로그 완전 소진 — 설치된 CLI 의 `--help` 출력을 probe 해 슬래시 명령 레지스트리를 자동 갱신 (best-effort, offline-first).**
|
|
6
|
+
|
|
7
|
+
### 배경
|
|
8
|
+
1단계(1.9.265)는 큐레이션 빌트인 레지스트리 + 사용자 override, 2단계(1.9.266)는 dispatch/handoff 자동 주입을 구축. 3단계는 처음부터 예정됐던 "항상 최신화 3중 경로"의 마지막 — CLI `--help` probe 자동 refresh. 외부 CLI 의 슬래시 명령은 버전마다 변동하므로, 큐레이션(빌트인)·수동(override)에 더해 *설치된 실제 CLI 에서 직접 회수*하는 경로를 완성.
|
|
9
|
+
|
|
10
|
+
### 구현
|
|
11
|
+
1. **`_parseSlashFromHelp(text, invoke)` 순수 파서** — `--help` 출력에서 슬래시 명령(`/cmd desc`) 또는 하위명령(들여쓰기 `cmd desc`) best-effort 추출. ANSI 색상 제거 · CLI 플래그(`--foo`)·옵션 의도적 제외 · cmd 길이 24자 가드 · 중복 제거. 부작용 0 → selftest 가능.
|
|
12
|
+
2. **`_probeAgentSlash(extDef, opts)`** — EXTERNAL_AGENTS 항목의 `bin` 을 `--help` 로 spawn (타임아웃 5s, `windowsHide`). copilot 처럼 subcommand 인 경우 `versionArgs` 의 base(예: `['copilot']`) 재사용. 실패(ENOENT)/0건 검출 시 `ok:false` + 한국어 reason → **호출부가 큐레이션 빌트인 유지(파괴적 덮어쓰기 방지)**.
|
|
13
|
+
3. **`_refreshAgentSlashCommands(root, targets, opts)`** — probe 성공 agent 만 `.harness/agent-slash-commands.json` 으로 병합 기록(나머지 보존), `note: "probed via …"` + `asOf: VERSION`. grok/ollama 등 EXTERNAL_AGENTS 미포함은 큐레이션/override 만.
|
|
14
|
+
4. **`slash-commands [agent] --refresh [--dry-run]` CLI** — 전체/단일 agent probe. dry-run 기본 미적용(실 기록은 `--refresh` 단독). `--json` 기계 판독.
|
|
15
|
+
5. **MCP `leerness_slash_commands` 확장** — `refresh`/`dryRun` 인자 추가(외부 AI 가 sub-agent 호출 전 레지스트리 자동 갱신).
|
|
16
|
+
6. **selftest 17 → 19** — `_parseSlashFromHelp` 슬래시 검출+플래그 제외 · subcommand 들여쓰기 파싱 2종 추가.
|
|
17
|
+
|
|
18
|
+
### 검증
|
|
19
|
+
- **selftest 19/19 PASS** · **E2E 217/217 PASS** (회귀 0).
|
|
20
|
+
- probe 성공 경로 단위 검증: 가짜 CLI(`--help` 슬래시 출력) → 2건 검출, `--version` 플래그 정상 제외.
|
|
21
|
+
- graceful fallback 검증: claude/codex/agy 미설치(ENOENT) + grok(대상 외) + copilot(gh ext 미설치, 0건) → 큐레이션 유지, 파일 미기록.
|
|
22
|
+
|
|
23
|
+
### UR-0021 완료
|
|
24
|
+
1단계(레지스트리/CLI/hint) → 2단계(dispatch/handoff 주입/MCP 73) → **3단계(--help probe 자동 refresh)** 로 "항상 최신화 3중 경로" 완성.
|
|
25
|
+
|
|
26
|
+
## 1.9.266 — 2026-06-01 — UR-0021 2단계: dispatch 슬래시 명령 자동 주입 + handoff 노출 + MCP 73
|
|
27
|
+
|
|
28
|
+
**🤖 서브에이전트 dispatch/handoff 에 각 에이전트 슬래시 명령을 자동 노출 — "알맞은 슬래시 명령으로 작업" (사용자 명시 UR-0021 2단계).**
|
|
29
|
+
|
|
30
|
+
### 배경
|
|
31
|
+
1단계(1.9.265)에서 레지스트리·CLI·`_agentSlashHint` 헬퍼를 구축. 2단계는 사용자 요구 핵심 — "서브에이전트 호출 시 각 에이전트에 알맞는 슬래시 명령이 적절히 사용" — 을 실제 dispatch 경로 + 세션 시작(handoff)에 연결.
|
|
32
|
+
|
|
33
|
+
### 구현
|
|
34
|
+
1. **`agents dispatch <task> --to <id>` 슬래시 힌트 주입** — 실행 명령 아래에 대상 에이전트 슬래시 명령 노출 (subcommand 타입은 "하위명령" 라벨).
|
|
35
|
+
2. **`agents multi` 슬래시 힌트** — 각 에이전트 명령마다 `🤖 슬래시: ...` 한 줄 + `--json` 에 `slashCommands` 필드 (`{ <id>: { invoke, commands[] } }`).
|
|
36
|
+
3. **handoff 본문 활성 에이전트 슬래시 섹션** — env flag(`LEERNESS_ENABLE_*`) 활성 에이전트별 슬래시 명령 요약. **spawn 없이 env 점검만** (handoff 속도 보존, 오버헤드 측정값 ~0). 활성 0이면 미노출.
|
|
37
|
+
4. **MCP 73번째 도구 `leerness_slash_commands`** — 외부 AI(메인)가 sub-agent 호출 전 각 에이전트 슬래시 명령 회수. 인자 `{ path?, agent? }`.
|
|
38
|
+
5. **grok 범위 메모**: grok 은 슬래시 레지스트리에 포함(CLI/MCP/hint 조회 가능)되나, 아직 `EXTERNAL_AGENTS` 정식 provider 는 아님 → dispatch/handoff 활성 목록은 claude/codex/agy/copilot/ollama 기준. grok 정식 provider 승격(install 흐름 1~5 선택지 확장 포함)은 별도 후속 task.
|
|
39
|
+
|
|
40
|
+
### stress-v211 — **21/21 PASS · 100%**
|
|
41
|
+
- 1.9.266 (8): MCP 73·handoff 노출/비노출·MCP round-trip(grok/전체)·dispatch·multi·json 주입 경로·hint
|
|
42
|
+
- 성능 (2): cold start + handoff 활성 섹션 오버헤드 측정(WITH−WITHOUT delta, ~0)
|
|
43
|
+
- 누적 회귀 (11): 1.9.207~265 (slash-commands CLI·레지스트리·record·shellGuard JSON·handoff 12필드·selftest 17·require.main·agy/gemini·ps5-chain·_isSecretKey·CJK)
|
|
44
|
+
|
|
3
45
|
## 1.9.265 — 2026-06-01 — UR-0021 1단계: CLI 에이전트 슬래시 명령어 레지스트리 (claude/codex/agy/grok/copilot)
|
|
4
46
|
|
|
5
47
|
**🤖 각 CLI AI 에이전트의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 호출 시 알맞게 참조 (사용자 명시 UR-0021 1단계).**
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.266 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║ 🤖
|
|
17
|
+
║ 🤖 dispatch 슬래시 주입 + MCP 73 (UR-0021 2단계 · R222) ║
|
|
18
18
|
╚══════════════════════════════════════════════════════════════╝
|
|
19
19
|
```
|
|
20
20
|
|
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.
|
|
10
|
+
const VERSION = '1.9.267';
|
|
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 처리).
|
|
@@ -2907,6 +2907,8 @@ function _selfTestCases() {
|
|
|
2907
2907
|
{ name: '_shellGuardAnalyze: bash && → 문제 없음', run: () => _shellGuardAnalyze('a && b', { shell: 'bash' }).issues.length === 0 },
|
|
2908
2908
|
{ name: 'AGENT_SLASH_COMMANDS: claude/codex/agy/grok/copilot 5종 + 명령 보유', run: () => { const ids = Object.keys(AGENT_SLASH_COMMANDS); return ['claude', 'codex', 'agy', 'grok', 'copilot'].every(id => ids.includes(id) && AGENT_SLASH_COMMANDS[id].commands.length > 0); } },
|
|
2909
2909
|
{ 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
|
+
{ 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
|
+
{ 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'); } },
|
|
2910
2912
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
2911
2913
|
];
|
|
2912
2914
|
}
|
|
@@ -3562,7 +3564,7 @@ function commandsCmd(root) {
|
|
|
3562
3564
|
{ cmd: 'resume [path]', desc: 'auto-resume-plan 적용 (1.9.203)' },
|
|
3563
3565
|
{ cmd: 'route <task-type>', desc: '작업 유형 분류 (11종)' },
|
|
3564
3566
|
{ cmd: 'agents list|check|quota|dispatch|multi', desc: '외부 AI CLI 오케스트레이션' },
|
|
3565
|
-
{ cmd: 'slash-commands [agent] [--json --record --detect]', desc: 'CLI 에이전트 슬래시 명령 레지스트리 (1.9.265, UR-0021)' },
|
|
3567
|
+
{ cmd: 'slash-commands [agent] [--json --record --detect --refresh]', desc: 'CLI 에이전트 슬래시 명령 레지스트리 + --help probe 자동 갱신 (1.9.265~267, UR-0021)' },
|
|
3566
3568
|
{ cmd: 'review-request "<request>"', desc: '사용자 요청 사전 검토 (1.9.176)' },
|
|
3567
3569
|
{ cmd: 'review <file> --persona <ids>', desc: '페르소나 리뷰 (1.9.29)' },
|
|
3568
3570
|
{ cmd: 'brainstorm "<topic>" [--include-code]', desc: '워크스페이스 회수 + 코드 grep' }
|
|
@@ -7956,6 +7958,31 @@ function handoff(root) {
|
|
|
7956
7958
|
}
|
|
7957
7959
|
} catch {}
|
|
7958
7960
|
|
|
7961
|
+
// 1.9.266 (UR-0021 2단계): 활성(env 활성화) CLI 에이전트별 슬래시 명령 요약 — sub-agent dispatch 시 알맞은 슬래시 참조
|
|
7962
|
+
// spawn 없이 env flag 만 점검 (handoff 속도 보존). 활성 에이전트 0 이면 미노출.
|
|
7963
|
+
try {
|
|
7964
|
+
const enabledAgents = EXTERNAL_AGENTS.filter(a => {
|
|
7965
|
+
const v = process.env[a.envFlag];
|
|
7966
|
+
return v && v !== '0' && String(v).toLowerCase() !== 'false';
|
|
7967
|
+
});
|
|
7968
|
+
if (enabledAgents.length > 0) {
|
|
7969
|
+
const isTtyS = process.stdout && process.stdout.isTTY;
|
|
7970
|
+
const cyS = s => isTtyS ? `\x1b[36m${s}\x1b[0m` : s;
|
|
7971
|
+
const dmS = s => isTtyS ? `\x1b[2m${s}\x1b[0m` : s;
|
|
7972
|
+
log('');
|
|
7973
|
+
log(cyS(`## 🤖 CLI 에이전트 슬래시 명령 (1.9.265~266, UR-0021)`));
|
|
7974
|
+
log(dmS(` 활성 에이전트 ${enabledAgents.length}개 — sub-agent 호출 시 각자 슬래시 명령 사용:`));
|
|
7975
|
+
for (const a of enabledAgents) {
|
|
7976
|
+
const hint = _agentSlashHint(root, a.id);
|
|
7977
|
+
if (hint && hint.commands.length) {
|
|
7978
|
+
const top = hint.commands.slice(0, 8).map(c => c.cmd).join(' ');
|
|
7979
|
+
log(dmS(` ${a.id.padEnd(8)} ${top}${hint.invoke === 'subcommand' ? ' (하위명령)' : ''}`));
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
log(dmS(` → 전체/기록/최신화: leerness slash-commands [agent] [--record]`));
|
|
7983
|
+
}
|
|
7984
|
+
} catch {}
|
|
7985
|
+
|
|
7959
7986
|
// 1.9.245: API skill cache 자동 참조 (사용자 명시 UR-0015)
|
|
7960
7987
|
// 현재 task 키워드 기반으로 .harness/api-skills/ 매칭 → 사용자가 정리해둔 API 문서 자동 노출
|
|
7961
7988
|
try {
|
|
@@ -10056,7 +10083,7 @@ const EXTERNAL_AGENTS = [
|
|
|
10056
10083
|
// 1.9.265 (사용자 명시 UR-0021): CLI AI 에이전트별 슬래시 명령어 레지스트리.
|
|
10057
10084
|
// 목적: 각 CLI (claude/codex/agy/grok/copilot) 의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 dispatch 시 알맞게 참조.
|
|
10058
10085
|
// "항상 최신화" 3중 경로: (1) 빌트인은 leerness 릴리스마다 갱신(asOf), (2) 사용자 .harness/agent-slash-commands.json override 병합,
|
|
10059
|
-
// (3) [
|
|
10086
|
+
// (3) [1.9.267 완료] `slash-commands --refresh` — 설치된 CLI 의 `--help` probe 로 자동 갱신(best-effort, 검출 0건이면 큐레이션 유지). 0-dep·offline-first.
|
|
10060
10087
|
// 주의: 외부 CLI 의 슬래시 명령은 버전마다 변동 → asOf 표기 + 사용자 override 우선. type='subcommand' 는 슬래시가 아닌 하위명령(예: gh copilot).
|
|
10061
10088
|
const AGENT_SLASH_COMMANDS = {
|
|
10062
10089
|
claude: {
|
|
@@ -10194,6 +10221,90 @@ function _agentSlashHint(root, agentId) {
|
|
|
10194
10221
|
summary: `${def.label}${invokeNote}: ${top}${def.commands.length > 8 ? ' …' : ''}`
|
|
10195
10222
|
};
|
|
10196
10223
|
}
|
|
10224
|
+
// 1.9.267 (UR-0021 3단계): CLI `--help` 출력에서 슬래시 명령/하위명령을 best-effort 파싱.
|
|
10225
|
+
// 순수 함수 (부작용 0, selftest 가능). invoke='slash' → `/cmd desc` 패턴, invoke='subcommand' → 들여쓰기 `cmd desc` 패턴.
|
|
10226
|
+
// CLI 플래그(--foo)·옵션은 의도적으로 제외. 검출 0건이면 큐레이션 빌트인을 그대로 유지(파괴적 덮어쓰기 방지).
|
|
10227
|
+
function _parseSlashFromHelp(text, invoke = 'slash') {
|
|
10228
|
+
const out = [];
|
|
10229
|
+
const seen = new Set();
|
|
10230
|
+
const lines = String(text || '').split(/\r?\n/);
|
|
10231
|
+
for (const raw of lines) {
|
|
10232
|
+
const ln = raw.replace(/\x1b\[[0-9;]*m/g, ''); // ANSI 색상 제거
|
|
10233
|
+
if (invoke === 'subcommand') {
|
|
10234
|
+
// 들여쓰기된 하위명령: " suggest 설명" — 슬래시/대시 시작 제외, 2칸+ 공백 구분
|
|
10235
|
+
const m = ln.match(/^\s{2,}([a-z][a-z0-9][\w-]*)\s{2,}(\S.*)$/);
|
|
10236
|
+
if (m && !/^--/.test(m[1])) {
|
|
10237
|
+
const cmd = m[1];
|
|
10238
|
+
if (!seen.has(cmd) && cmd.length <= 24) { seen.add(cmd); out.push({ cmd, desc: m[2].trim().slice(0, 80) }); }
|
|
10239
|
+
}
|
|
10240
|
+
continue;
|
|
10241
|
+
}
|
|
10242
|
+
// 슬래시 명령: "/help 설명" 또는 " /help - 설명"
|
|
10243
|
+
const m = ln.match(/^\s*(\/[a-zA-Z][\w-]*)(?:\s+[-–:]?\s*(.*))?$/);
|
|
10244
|
+
if (m) {
|
|
10245
|
+
const cmd = m[1];
|
|
10246
|
+
if (!seen.has(cmd) && cmd.length <= 24) { seen.add(cmd); out.push({ cmd, desc: (m[2] || '').trim().slice(0, 80) }); }
|
|
10247
|
+
}
|
|
10248
|
+
}
|
|
10249
|
+
return out;
|
|
10250
|
+
}
|
|
10251
|
+
// 1.9.267 (UR-0021 3단계): 설치된 CLI 의 `--help` 를 spawn 해 슬래시 명령을 probe (best-effort, offline-first).
|
|
10252
|
+
// extDef: EXTERNAL_AGENTS 항목 (bin/versionArgs 보유). copilot 처럼 subcommand 인 경우 versionArgs 의 base(예: ['copilot']) + '--help'.
|
|
10253
|
+
// 실패/0건이면 ok:false + reason — 호출부가 큐레이션 빌트인 유지. 타임아웃 5s (행 방지).
|
|
10254
|
+
function _probeAgentSlash(extDef, opts = {}) {
|
|
10255
|
+
const invoke = opts.invoke || 'slash';
|
|
10256
|
+
const timeout = opts.timeout || 5000;
|
|
10257
|
+
if (!extDef || !extDef.bin) return { ok: false, commands: [], reason: 'probe 대상 정보 없음' };
|
|
10258
|
+
// versionArgs 가 ['copilot','--version'] 처럼 base 를 가지면 그 base 를 help 에 재사용
|
|
10259
|
+
const base = Array.isArray(extDef.versionArgs) ? extDef.versionArgs.filter(a => !/^--?(version|v)$/i.test(a)) : [];
|
|
10260
|
+
const probeArgs = [...base, '--help'];
|
|
10261
|
+
let r;
|
|
10262
|
+
try {
|
|
10263
|
+
r = cp.spawnSync(extDef.bin, probeArgs, { encoding: 'utf8', timeout, windowsHide: true });
|
|
10264
|
+
} catch (e) {
|
|
10265
|
+
return { ok: false, commands: [], reason: `spawn 예외: ${e.message}` };
|
|
10266
|
+
}
|
|
10267
|
+
if (r.error) return { ok: false, commands: [], reason: `spawn 실패: ${r.error.code || r.error.message}` };
|
|
10268
|
+
const text = `${r.stdout || ''}\n${r.stderr || ''}`;
|
|
10269
|
+
const commands = _parseSlashFromHelp(text, invoke);
|
|
10270
|
+
if (!commands.length) return { ok: false, commands: [], reason: `${extDef.bin} --help 출력에서 ${invoke === 'subcommand' ? '하위명령' : '슬래시 명령'} 미검출 (큐레이션 유지)` };
|
|
10271
|
+
return { ok: true, commands, reason: `${commands.length}건 검출`, probeArgs: probeArgs.join(' ') };
|
|
10272
|
+
}
|
|
10273
|
+
// 1.9.267 (UR-0021 3단계): probe 결과를 .harness/agent-slash-commands.json 으로 병합 기록 (성공 agent 만 갱신, 나머지 보존).
|
|
10274
|
+
function _refreshAgentSlashCommands(root, targets, opts = {}) {
|
|
10275
|
+
const merged = _loadAgentSlashCommands(root); // 현재(builtin+user) 상태 보존 기반
|
|
10276
|
+
const results = [];
|
|
10277
|
+
const agents = {};
|
|
10278
|
+
for (const [id, d] of Object.entries(merged)) {
|
|
10279
|
+
agents[id] = { label: d.label, asOf: d.asOf, invoke: d.invoke, note: d.note || undefined, commands: d.commands };
|
|
10280
|
+
}
|
|
10281
|
+
for (const id of targets) {
|
|
10282
|
+
const ext = EXTERNAL_AGENTS.find(a => a.id === id);
|
|
10283
|
+
const def = merged[id] || AGENT_SLASH_COMMANDS[id];
|
|
10284
|
+
if (!ext) { results.push({ id, ok: false, reason: 'probe 대상 아님 (EXTERNAL_AGENTS 미포함 — grok/ollama 등은 큐레이션/override 만)' }); continue; }
|
|
10285
|
+
const pr = _probeAgentSlash(ext, { invoke: (def && def.invoke) || 'slash', timeout: opts.timeout });
|
|
10286
|
+
if (pr.ok) {
|
|
10287
|
+
agents[id] = {
|
|
10288
|
+
label: (def && def.label) || id,
|
|
10289
|
+
asOf: VERSION,
|
|
10290
|
+
invoke: (def && def.invoke) || 'slash',
|
|
10291
|
+
note: `probed via "${ext.bin} ${pr.probeArgs}" @ ${new Date().toISOString().slice(0, 10)}`,
|
|
10292
|
+
commands: pr.commands
|
|
10293
|
+
};
|
|
10294
|
+
results.push({ id, ok: true, count: pr.commands.length, reason: pr.reason });
|
|
10295
|
+
} else {
|
|
10296
|
+
results.push({ id, ok: false, reason: pr.reason });
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
const anyOk = results.some(r => r.ok);
|
|
10300
|
+
let file = null;
|
|
10301
|
+
if (anyOk && !opts.dryRun) {
|
|
10302
|
+
file = _agentSlashFile(root);
|
|
10303
|
+
mkdirp(path.dirname(file));
|
|
10304
|
+
writeUtf8(file, JSON.stringify({ schemaVersion: 1, recordedAt: new Date().toISOString(), refreshedAt: new Date().toISOString(), agents }, null, 2) + '\n');
|
|
10305
|
+
}
|
|
10306
|
+
return { results, file, anyOk };
|
|
10307
|
+
}
|
|
10197
10308
|
// 1.9.265 (UR-0021): leerness slash-commands [agent] [--json --record --detect]
|
|
10198
10309
|
// 인자 없음 → 전체 에이전트 슬래시 명령 목록. <agent> → 단일. --record → 워크스페이스 기록. --detect → 설치된 CLI 만 표시.
|
|
10199
10310
|
function slashCommandsCmd(root, agentArg, opts = {}) {
|
|
@@ -10215,6 +10326,28 @@ function slashCommandsCmd(root, agentArg, opts = {}) {
|
|
|
10215
10326
|
log(` → 사용자 편집으로 최신화 가능 (agents.<id>.commands 배열)`);
|
|
10216
10327
|
return;
|
|
10217
10328
|
}
|
|
10329
|
+
// 1.9.267 (UR-0021 3단계): --refresh — 설치된 CLI 의 `--help` probe 로 레지스트리 자동 갱신.
|
|
10330
|
+
if (opts.refresh) {
|
|
10331
|
+
const targets = agentArg ? [agentArg] : Object.keys(all);
|
|
10332
|
+
const ref = _refreshAgentSlashCommands(root, targets, { dryRun: opts.dryRun });
|
|
10333
|
+
if (opts.json) {
|
|
10334
|
+
log(JSON.stringify({ refreshed: ref.anyOk && !opts.dryRun, dryRun: !!opts.dryRun, file: ref.file, results: ref.results }, null, 2));
|
|
10335
|
+
return;
|
|
10336
|
+
}
|
|
10337
|
+
const isTty0 = process.stdout && process.stdout.isTTY;
|
|
10338
|
+
const g0 = s => isTty0 ? `\x1b[32m${s}\x1b[0m` : s;
|
|
10339
|
+
const y0 = s => isTty0 ? `\x1b[33m${s}\x1b[0m` : s;
|
|
10340
|
+
const d0 = s => isTty0 ? `\x1b[2m${s}\x1b[0m` : s;
|
|
10341
|
+
log(`# leerness slash-commands --refresh (1.9.267, UR-0021 3단계)${opts.dryRun ? ' [dry-run]' : ''}`);
|
|
10342
|
+
for (const r of ref.results) {
|
|
10343
|
+
if (r.ok) log(` ${g0('✓')} ${r.id} — ${r.count}건 probe 검출`);
|
|
10344
|
+
else log(` ${y0('·')} ${r.id} — ${d0(r.reason)}`);
|
|
10345
|
+
}
|
|
10346
|
+
if (ref.anyOk && !opts.dryRun) log(g0(`\n✓ 갱신 기록: ${ref.file}`));
|
|
10347
|
+
else if (ref.anyOk && opts.dryRun) log(d0(`\n(dry-run — 기록 안 함. --refresh 만 주면 실제 기록)`));
|
|
10348
|
+
else log(y0(`\n⚠ probe 검출 0 — 큐레이션 빌트인 유지 (CLI 미설치 또는 --help 에 슬래시 명령 미노출)`));
|
|
10349
|
+
return;
|
|
10350
|
+
}
|
|
10218
10351
|
const ids = agentArg ? [agentArg] : Object.keys(all);
|
|
10219
10352
|
if (agentArg && !all[agentArg]) {
|
|
10220
10353
|
if (opts.json) { log(JSON.stringify({ error: `unknown agent: ${agentArg}`, known: Object.keys(all) }, null, 2)); return; }
|
|
@@ -10248,7 +10381,7 @@ function slashCommandsCmd(root, agentArg, opts = {}) {
|
|
|
10248
10381
|
for (const c of d.commands) log(` ${gr((c.cmd || '').padEnd(14))} ${dm(c.desc || '')}`);
|
|
10249
10382
|
log('');
|
|
10250
10383
|
}
|
|
10251
|
-
log(dm(` 기록: leerness slash-commands --record · 단일: leerness slash-commands claude · 설치 감지: --detect`));
|
|
10384
|
+
log(dm(` 기록: leerness slash-commands --record · 단일: leerness slash-commands claude · 설치 감지: --detect · 자동 갱신: --refresh [--dry-run]`));
|
|
10252
10385
|
}
|
|
10253
10386
|
|
|
10254
10387
|
// 1.9.157: Provider Registry — 사용자 정의 provider 동적 추가 (.harness/providers.json)
|
|
@@ -11312,7 +11445,9 @@ function agentsCmd(root, sub, ...args) {
|
|
|
11312
11445
|
log(JSON.stringify({
|
|
11313
11446
|
task, count: ready.length,
|
|
11314
11447
|
agents: ready.map(x => ({ id: x.def.id, version: x.status.version })),
|
|
11315
|
-
commands: ready.map(x => _dispatchCommand(x.def.id, task, writeMode))
|
|
11448
|
+
commands: ready.map(x => _dispatchCommand(x.def.id, task, writeMode)),
|
|
11449
|
+
// 1.9.266 (UR-0021 2단계): 각 에이전트 슬래시 명령 힌트 — sub-agent 가 알맞은 슬래시 사용
|
|
11450
|
+
slashCommands: ready.reduce((acc, x) => { const h = _agentSlashHint(root, x.def.id); if (h) acc[x.def.id] = { invoke: h.invoke, commands: h.commands.map(c => c.cmd) }; return acc; }, {})
|
|
11316
11451
|
}, null, 2));
|
|
11317
11452
|
return;
|
|
11318
11453
|
}
|
|
@@ -11328,6 +11463,11 @@ function agentsCmd(root, sub, ...args) {
|
|
|
11328
11463
|
log('```sh');
|
|
11329
11464
|
log(_dispatchCommand(def.id, task, writeMode));
|
|
11330
11465
|
log('```');
|
|
11466
|
+
// 1.9.266 (UR-0021 2단계): 에이전트별 슬래시 명령 힌트
|
|
11467
|
+
try {
|
|
11468
|
+
const hint = _agentSlashHint(root, def.id);
|
|
11469
|
+
if (hint && hint.commands.length) log(` 🤖 슬래시: ${hint.commands.slice(0, 8).map(c => c.cmd).join(' ')}${hint.invoke === 'subcommand' ? ' (하위명령)' : ''}`);
|
|
11470
|
+
} catch {}
|
|
11331
11471
|
log('');
|
|
11332
11472
|
}
|
|
11333
11473
|
log('## 정책 (1.9.152 / 1.9.156)');
|
|
@@ -11388,6 +11528,17 @@ function agentsCmd(root, sub, ...args) {
|
|
|
11388
11528
|
} else if (target === 'copilot') {
|
|
11389
11529
|
log(`gh copilot suggest "${q}"`);
|
|
11390
11530
|
}
|
|
11531
|
+
// 1.9.266 (UR-0021 2단계): 대상 에이전트의 슬래시 명령 힌트 — sub-agent 작업 시 알맞은 슬래시 명령 참조
|
|
11532
|
+
try {
|
|
11533
|
+
const hint = _agentSlashHint(root, target);
|
|
11534
|
+
if (hint && hint.commands.length) {
|
|
11535
|
+
log('');
|
|
11536
|
+
log(`## 🤖 ${target} 슬래시 명령 (1.9.265, UR-0021)`);
|
|
11537
|
+
if (hint.invoke === 'subcommand') log(` ※ 슬래시가 아닌 하위명령: ${hint.commands.map(c => c.cmd).join(' / ')}`);
|
|
11538
|
+
else log(` 세션 내 사용 가능: ${hint.commands.slice(0, 10).map(c => c.cmd).join(' ')}`);
|
|
11539
|
+
log(` → 전체/기록: leerness slash-commands ${target} [--record]`);
|
|
11540
|
+
}
|
|
11541
|
+
} catch {}
|
|
11391
11542
|
log('');
|
|
11392
11543
|
log(`## 정책 (1.9.36)`);
|
|
11393
11544
|
log(` - leerness는 외부 CLI를 자동 호출하지 않음 (사용자 명시적 실행)`);
|
|
@@ -16308,7 +16459,8 @@ function mcpServeCmd(root) {
|
|
|
16308
16459
|
{ name: 'leerness_env_info', description: '1.9.241 (사용자 명시 UR-0014) — 환경 종합 정보 회수. OS / 언어 (LANG, 코드페이지) / 한국어 Windows / 하드웨어 / 터미널 (TTY, PowerShell 버전) / 도구 버전 (git, npm, python). 외부 AI 가 환경 호환성을 미리 인지 → 인코딩 오류 예방. 응답: { os, node, locale, hardware, terminal, tools }. 인자: { path?, encodingCheck? }. encodingCheck: true 시 셸 스크립트 (.ps1/.bat/.cmd/.sh) BOM 없는 비-ASCII 위험 감지', inputSchema: { type: 'object', properties: { path: { type: 'string' }, encodingCheck: { type: 'boolean' } } } },
|
|
16309
16460
|
{ name: 'leerness_api_skill', description: '1.9.245 (사용자 명시 UR-0015) — API 문서·관련링크 자동 캐시. 공식 API 문서 URL을 fetch 하고 1단계 same-domain 관련 링크까지 정리 → .harness/api-skills/<id>.md 저장. 후속 같은 API 관련 작업 시 자동 참조. 응답: list (skills 배열) / show (전체 본문) / match (task 매칭 결과). 인자: { path?, sub ("list"|"show"|"match"|"add"|"drop"), url? (add), id? (show/drop), query? (match), direction? (add: 구현 방향 텍스트) }. 외부 AI가 "이 프로젝트 어떤 API 문서가 정리되어 있나?" / "내 작업과 매칭되는 API skill 있나?" 회수.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['list', 'show', 'match', 'add', 'drop'] }, url: { type: 'string' }, id: { type: 'string' }, query: { type: 'string' }, direction: { type: 'string' } }, required: ['sub'] } },
|
|
16310
16461
|
{ name: 'leerness_selftest', description: '1.9.258/259 — 설치된 leerness 바이너리의 코어 함수(보안 _isSecretKey / 버전 compareVer / 인코딩 _classifyCJK 등) 무결성 자가 검증. 응답: { version, total, pass, fail, ok, results[] }. 외부 AI/CI 가 "이 leerness 설치가 정상인가?(npx 캐시 손상·부분 설치 감지)" 를 1초 내 확인. 인자: 없음.', inputSchema: { type: 'object', properties: {} } },
|
|
16311
|
-
{ name: 'leerness_shell_guard', description: '1.9.260/261 (사용자 명시 UR-0020) — 터미널 명령 셸 호환성 린터. 외부 AI 가 명령을 실행하기 전에 셸 호환성 문제를 사전 점검. 예: Windows PowerShell 5.1 은 && / || 미지원 → A; if ($?) { B } 제안. 6 규칙(ps5-chain/ps-devnull/ps-inline-env/ps-rm-rf/cmd-semicolon/ps-version-unknown) + 과거 실패 회수(.harness/shell-failures.json). 응답: { shell, psVersion, issues[{rule,severity,detail,suggestion}], pastSame, pastSimilar, ok }. 인자: { command (required), path? }. 현재 셸/PS 버전 자동 감지.', inputSchema: { type: 'object', properties: { command: { type: 'string' }, path: { type: 'string' } }, required: ['command'] } }
|
|
16462
|
+
{ name: 'leerness_shell_guard', description: '1.9.260/261 (사용자 명시 UR-0020) — 터미널 명령 셸 호환성 린터. 외부 AI 가 명령을 실행하기 전에 셸 호환성 문제를 사전 점검. 예: Windows PowerShell 5.1 은 && / || 미지원 → A; if ($?) { B } 제안. 6 규칙(ps5-chain/ps-devnull/ps-inline-env/ps-rm-rf/cmd-semicolon/ps-version-unknown) + 과거 실패 회수(.harness/shell-failures.json). 응답: { shell, psVersion, issues[{rule,severity,detail,suggestion}], pastSame, pastSimilar, ok }. 인자: { command (required), path? }. 현재 셸/PS 버전 자동 감지.', inputSchema: { type: 'object', properties: { command: { type: 'string' }, path: { type: 'string' } }, required: ['command'] } },
|
|
16463
|
+
{ name: 'leerness_slash_commands', description: '1.9.265/266 (사용자 명시 UR-0021) — CLI AI 에이전트별 슬래시 명령어 레지스트리. 외부 AI(메인)가 sub-agent(codex/agy/claude/grok/copilot)를 호출할 때 각 에이전트에 알맞는 슬래시 명령을 참조. 빌트인 + 사용자 .harness/agent-slash-commands.json override 병합. 응답: { agents: { <id>: { label, asOf, invoke(slash|subcommand), note, source, count, commands[{cmd,desc}] } } }. 인자: { path?, agent? (생략 시 전체), refresh? (1.9.267 — 설치된 CLI --help probe 자동 갱신), dryRun? }. agent 지정 시 단일.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, agent: { type: 'string' }, refresh: { type: 'boolean' }, dryRun: { type: 'boolean' } } } }
|
|
16312
16464
|
];
|
|
16313
16465
|
|
|
16314
16466
|
function send(obj) {
|
|
@@ -16519,6 +16671,12 @@ function mcpServeCmd(root) {
|
|
|
16519
16671
|
// 1.9.261 (1.9.260, UR-0020): 터미널 명령 셸 호환성 린터
|
|
16520
16672
|
cliArgs = ['shell-guard', String(args.command || ''), '--path', targetPath, '--json'];
|
|
16521
16673
|
break;
|
|
16674
|
+
case 'leerness_slash_commands':
|
|
16675
|
+
// 1.9.266 (1.9.265, UR-0021): CLI 에이전트 슬래시 명령 레지스트리
|
|
16676
|
+
cliArgs = ['slash-commands', ...(args.agent ? [String(args.agent)] : []), '--path', targetPath, '--json'];
|
|
16677
|
+
if (args.refresh === true) cliArgs.push('--refresh');
|
|
16678
|
+
if (args.dryRun === true) cliArgs.push('--dry-run');
|
|
16679
|
+
break;
|
|
16522
16680
|
default:
|
|
16523
16681
|
return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
|
|
16524
16682
|
}
|
|
@@ -20400,7 +20558,7 @@ async function main() {
|
|
|
20400
20558
|
// 1.9.265: leerness slash-commands [agent] — CLI 에이전트별 슬래시 명령어 레지스트리 (사용자 명시 UR-0021)
|
|
20401
20559
|
if (cmd === 'slash-commands' || cmd === 'slash' || cmd === 'agent-slash') {
|
|
20402
20560
|
const agentArg = args[1] && !args[1].startsWith('-') ? args[1] : null;
|
|
20403
|
-
return slashCommandsCmd(arg('--path', process.cwd()), agentArg, { json: has('--json'), record: has('--record'), force: has('--force'), detect: has('--detect') });
|
|
20561
|
+
return slashCommandsCmd(arg('--path', process.cwd()), agentArg, { json: has('--json'), record: has('--record'), force: has('--force'), detect: has('--detect'), refresh: has('--refresh'), dryRun: has('--dry-run') });
|
|
20404
20562
|
}
|
|
20405
20563
|
// 1.9.245: API skill cache — 공식 문서·관련링크 자동 정리 (사용자 명시 UR-0015)
|
|
20406
20564
|
if (cmd === 'api-skill') return apiSkillCmd(arg('--path', process.cwd()), args[1] || 'help');
|
|
@@ -20569,5 +20727,6 @@ module.exports = {
|
|
|
20569
20727
|
// 1.9.263: shell 실패 메모리 + 환경 버전 변동 (UR-0020 3단계) — handoff 통합 단위 테스트
|
|
20570
20728
|
_shellFailuresPath, _loadShellFailures, _recordShellFailure, _shellEnvDrift,
|
|
20571
20729
|
// 1.9.265: CLI 에이전트 슬래시 명령어 레지스트리 (UR-0021 1단계) — 단위 테스트
|
|
20572
|
-
AGENT_SLASH_COMMANDS, _agentSlashFile, _loadAgentSlashCommands, _recordAgentSlashCommands, _agentSlashHint, slashCommandsCmd
|
|
20730
|
+
AGENT_SLASH_COMMANDS, _agentSlashFile, _loadAgentSlashCommands, _recordAgentSlashCommands, _agentSlashHint, slashCommandsCmd,
|
|
20731
|
+
_parseSlashFromHelp, _probeAgentSlash, _refreshAgentSlashCommands
|
|
20573
20732
|
};
|