leerness 1.9.263 → 1.9.265
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 +258 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.265 — 2026-06-01 — UR-0021 1단계: CLI 에이전트 슬래시 명령어 레지스트리 (claude/codex/agy/grok/copilot)
|
|
4
|
+
|
|
5
|
+
**🤖 각 CLI AI 에이전트의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 호출 시 알맞게 참조 (사용자 명시 UR-0021 1단계).**
|
|
6
|
+
|
|
7
|
+
### 배경
|
|
8
|
+
codex/agy/claude/grok 등 CLI 로 구동되는 AI 에이전트는 각자 다른 슬래시 명령(`/init`, `/compact`, `/diff` …)을 쓴다. 서브에이전트로 dispatch 할 때 각 에이전트에 맞는 슬래시 명령을 알맞게 써야 작업이 제대로 된다. 이를 위해 명령어를 항상 최신화·기록하는 레지스트리가 필요. (review-request: ✓ 진행 안전 / reuse: 기존 레지스트리 없음 — 신규)
|
|
9
|
+
|
|
10
|
+
### 구현 (1단계 — 레지스트리 + CLI + dispatch 헬퍼)
|
|
11
|
+
1. **`AGENT_SLASH_COMMANDS` 빌트인 레지스트리** — claude(13)/codex(10)/agy(6)/**grok(6)**/copilot(3). 각 명령 `{cmd, desc}` + `asOf` 버전 + `invoke`(slash|subcommand) + `note`.
|
|
12
|
+
- **Grok CLI** 사용자 명시 반영 (`/help /clear /model /new /login /exit`)
|
|
13
|
+
- copilot 은 슬래시가 아닌 `gh copilot <sub>` 하위명령 → `invoke: 'subcommand'` 라벨
|
|
14
|
+
2. **항상 최신화 3중 경로**: (1) 빌트인은 릴리스마다 갱신(`asOf`), (2) 사용자 `.harness/agent-slash-commands.json` override 병합(기존 agent 명령 교체 + 신규 agent 추가), (3) [2~3단계 예정] CLI `--help` probe 자동 refresh.
|
|
15
|
+
3. **`leerness slash-commands [agent]` CLI**: 전체/단일 목록 · `--json` · `--record`(워크스페이스 기록) · `--detect`(설치된 CLI 표시) · 별칭 `slash`/`agent-slash`.
|
|
16
|
+
4. **`_agentSlashHint(root, agentId)` 헬퍼 export** — 서브에이전트 dispatch 시 주입할 1줄 요약 + 명령 배열 (2단계에서 agents multi/agent 가 활용).
|
|
17
|
+
5. **selftest 2종 추가** (15→17): 레지스트리 5종 보유 + hint 요약/하위명령 라벨.
|
|
18
|
+
|
|
19
|
+
### 2~3단계 예정
|
|
20
|
+
- 2단계: `agents multi`/`agent` dispatch 시 대상 에이전트 슬래시 명령 자동 주입 + handoff 노출
|
|
21
|
+
- 3단계: 설치된 CLI 의 `--help`/`/help` 를 probe 해 레지스트리 자동 refresh
|
|
22
|
+
|
|
23
|
+
### stress-v210 — **26/26 PASS · 100%**
|
|
24
|
+
- 1.9.265 (13): 레지스트리 5종·grok·load/merge(builtin/user/신규)·record·hint·copilot subcommand·CLI list/json/record/unknown
|
|
25
|
+
- 성능 (2): cold start avg 370ms · load 100회 4ms
|
|
26
|
+
- 누적 회귀 (11): 1.9.207~264 (selftest 17·shellGuard JSON·handoff 12필드·MCP 72·shell-guard·require.main·agy/gemini·CJK·_isSecretKey·path-setup)
|
|
27
|
+
|
|
28
|
+
## 1.9.264 — 2026-06-01 — shellGuard JSON 12번째 통합 필드 (handoff/session close/health) + session close 본문 셸 요약
|
|
29
|
+
|
|
30
|
+
**🐚 UR-0020 셸 실패 메모리를 `--json` 표면 3 명령에 일관되게 통합 — 외부 AI/자동화가 셸 호환성 상태를 구조적으로 소비.**
|
|
31
|
+
|
|
32
|
+
### 배경
|
|
33
|
+
1.9.263에서 셸 실패 메모리를 handoff *본문 텍스트*에 노출했으나, `--json` 표면에는 없어 외부 AI/CI 가 구조적으로 못 읽음. leerness JSON 표면은 handoff/session close/health 3 명령이 동일 필드를 carry 하는 일관성 패턴(1.9.227/230/234 등)이 확립돼 있음 — `shellGuard` 를 12번째 필드로 추가해 그 패턴을 완성.
|
|
34
|
+
|
|
35
|
+
### 구현
|
|
36
|
+
1. **`shellGuard` JSON 12번째 통합 필드** — handoff/session close/health 3 명령 모두 동일 스키마:
|
|
37
|
+
- `failureCount` · `recent`(최근 3건: cmd 50자/exitCode/shell/rules) · `envDriftChanges` · `envDrift`(node/PS 버전 변동 배열 또는 null)
|
|
38
|
+
2. **session close 본문 셸 요약** (1.9.217 통합 보고에 추가): 실패 누적 시 "🐚 셸 실패 누적 N건 — 다음 handoff 가 자동 노출" + env drift 경고, 깨끗하면 "✓ 셸 실패 기록 없음". `_loadShellFailures`/`_shellEnvDrift` 재사용 (1.9.263 export).
|
|
39
|
+
|
|
40
|
+
### stress-v209 — **22/22 PASS · 100%**
|
|
41
|
+
- 1.9.264 (9): shellGuard 스키마·실패 반영·3 명령 일관성·envDrift 반영·session close 본문 노출/비노출
|
|
42
|
+
- 성능 (2): cold start avg 403ms · handoff --json 1540ms
|
|
43
|
+
- 누적 회귀 (11): 1.9.207~263 (handoff 본문 셸 가드·JSON 12 필드·MCP 72·shell-guard·selftest 15·require.main·path-setup·CJK·_isSecretKey·posixEncoding)
|
|
44
|
+
|
|
3
45
|
## 1.9.263 — 2026-06-01 — UR-0020 3단계: handoff 셸 실패 메모리 + 환경 버전 변동 자동 노출
|
|
4
46
|
|
|
5
47
|
**🐚 과거 터미널 셸 실패 기록 + 환경 버전 변동을 매 세션 handoff 본문에 자동 표면화 (사용자 명시 UR-0020 완성).**
|
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.265 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║
|
|
17
|
+
║ 🤖 CLI 에이전트 슬래시 명령 레지스트리 (UR-0021 · R221) ║
|
|
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.265';
|
|
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 처리).
|
|
@@ -2905,6 +2905,8 @@ function _selfTestCases() {
|
|
|
2905
2905
|
{ name: '_unixPathBlock: 멱등 마커 + export', run: () => { const b = _unixPathBlock('/x/bin'); return b.includes('managed') && /export PATH="\$PATH:\/x\/bin"/.test(b); } },
|
|
2906
2906
|
{ name: '_shellGuardAnalyze: PS5.1 && → ps5-chain error', run: () => { const r = _shellGuardAnalyze('a && b', { shell: 'powershell', psVersion: '5' }); return r.issues.some(i => i.rule === 'ps5-chain' && i.severity === 'error'); } },
|
|
2907
2907
|
{ name: '_shellGuardAnalyze: bash && → 문제 없음', run: () => _shellGuardAnalyze('a && b', { shell: 'bash' }).issues.length === 0 },
|
|
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
|
+
{ 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); } },
|
|
2908
2910
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
2909
2911
|
];
|
|
2910
2912
|
}
|
|
@@ -3560,6 +3562,7 @@ function commandsCmd(root) {
|
|
|
3560
3562
|
{ cmd: 'resume [path]', desc: 'auto-resume-plan 적용 (1.9.203)' },
|
|
3561
3563
|
{ cmd: 'route <task-type>', desc: '작업 유형 분류 (11종)' },
|
|
3562
3564
|
{ 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)' },
|
|
3563
3566
|
{ cmd: 'review-request "<request>"', desc: '사용자 요청 사전 검토 (1.9.176)' },
|
|
3564
3567
|
{ cmd: 'review <file> --persona <ids>', desc: '페르소나 리뷰 (1.9.29)' },
|
|
3565
3568
|
{ cmd: 'brainstorm "<topic>" [--include-code]', desc: '워크스페이스 회수 + 코드 grep' }
|
|
@@ -7515,6 +7518,17 @@ function handoff(root) {
|
|
|
7515
7518
|
ids: allSkills.slice(0, 10).map(s => s.id)
|
|
7516
7519
|
};
|
|
7517
7520
|
} catch {}
|
|
7521
|
+
// 1.9.264: shellGuard 통합 (handoff JSON 12번째 통합 필드) — UR-0020 셸 실패 메모리 + 환경 변동 외부 회수
|
|
7522
|
+
try {
|
|
7523
|
+
const sf = _loadShellFailures(root);
|
|
7524
|
+
const drift = _shellEnvDrift(root);
|
|
7525
|
+
result.shellGuard = {
|
|
7526
|
+
failureCount: sf.failures.length,
|
|
7527
|
+
recent: sf.failures.slice(-3).map(f => ({ cmd: (f.cmd || '').slice(0, 50), exitCode: f.exitCode, shell: f.shell, rules: f.issues || [] })),
|
|
7528
|
+
envDriftChanges: drift && drift.changes ? drift.changes.length : 0,
|
|
7529
|
+
envDrift: drift ? drift.changes : null
|
|
7530
|
+
};
|
|
7531
|
+
} catch {}
|
|
7518
7532
|
} catch {}
|
|
7519
7533
|
try {
|
|
7520
7534
|
const pwState = _loadPreWakeReport(root);
|
|
@@ -10039,6 +10053,204 @@ const EXTERNAL_AGENTS = [
|
|
|
10039
10053
|
installCmd: 'curl -fsSL https://ollama.com/install.sh | sh (또는 https://ollama.com/download)', installHint: 'ollama serve 실행 + ollama pull <model>' }
|
|
10040
10054
|
];
|
|
10041
10055
|
|
|
10056
|
+
// 1.9.265 (사용자 명시 UR-0021): CLI AI 에이전트별 슬래시 명령어 레지스트리.
|
|
10057
|
+
// 목적: 각 CLI (claude/codex/agy/grok/copilot) 의 슬래시 명령어를 큐레이션·기록하고, 서브에이전트 dispatch 시 알맞게 참조.
|
|
10058
|
+
// "항상 최신화" 3중 경로: (1) 빌트인은 leerness 릴리스마다 갱신(asOf), (2) 사용자 .harness/agent-slash-commands.json override 병합,
|
|
10059
|
+
// (3) [2~3단계 예정] CLI `--help`/`/help` probe 자동 refresh. 0-dep·offline-first 라서 1단계는 (1)+(2) 큐레이션 기반.
|
|
10060
|
+
// 주의: 외부 CLI 의 슬래시 명령은 버전마다 변동 → asOf 표기 + 사용자 override 우선. type='subcommand' 는 슬래시가 아닌 하위명령(예: gh copilot).
|
|
10061
|
+
const AGENT_SLASH_COMMANDS = {
|
|
10062
|
+
claude: {
|
|
10063
|
+
label: 'Anthropic Claude Code', asOf: '1.9.265', invoke: 'slash',
|
|
10064
|
+
commands: [
|
|
10065
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
10066
|
+
{ cmd: '/clear', desc: '대화 컨텍스트 초기화' },
|
|
10067
|
+
{ cmd: '/compact', desc: '대화 압축 (컨텍스트 절약)' },
|
|
10068
|
+
{ cmd: '/init', desc: 'CLAUDE.md 생성/갱신' },
|
|
10069
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
10070
|
+
{ cmd: '/review', desc: 'PR/변경 리뷰' },
|
|
10071
|
+
{ cmd: '/agents', desc: '서브에이전트 관리' },
|
|
10072
|
+
{ cmd: '/mcp', desc: 'MCP 서버 상태/관리' },
|
|
10073
|
+
{ cmd: '/memory', desc: '메모리 파일 편집' },
|
|
10074
|
+
{ cmd: '/permissions', desc: '권한 설정' },
|
|
10075
|
+
{ cmd: '/cost', desc: '토큰 비용 표시' },
|
|
10076
|
+
{ cmd: '/resume', desc: '이전 세션 재개' },
|
|
10077
|
+
{ cmd: '/config', desc: '설정' }
|
|
10078
|
+
]
|
|
10079
|
+
},
|
|
10080
|
+
codex: {
|
|
10081
|
+
label: 'OpenAI Codex CLI', asOf: '1.9.265', invoke: 'slash',
|
|
10082
|
+
commands: [
|
|
10083
|
+
{ cmd: '/init', desc: '프로젝트 컨텍스트 초기화' },
|
|
10084
|
+
{ cmd: '/compact', desc: '대화 압축' },
|
|
10085
|
+
{ cmd: '/diff', desc: '작업 트리 diff 표시' },
|
|
10086
|
+
{ cmd: '/mention', desc: '파일 멘션/첨부' },
|
|
10087
|
+
{ cmd: '/status', desc: '세션 상태/사용량' },
|
|
10088
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
10089
|
+
{ cmd: '/approvals', desc: '승인 모드 변경 (sandbox)' },
|
|
10090
|
+
{ cmd: '/new', desc: '새 대화 시작' },
|
|
10091
|
+
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
10092
|
+
{ cmd: '/quit', desc: '종료' }
|
|
10093
|
+
]
|
|
10094
|
+
},
|
|
10095
|
+
agy: {
|
|
10096
|
+
label: 'Google Antigravity CLI', asOf: '1.9.265', invoke: 'slash',
|
|
10097
|
+
note: '큐레이션(best-effort) — agy CLI 는 신규 도구라 슬래시 명령이 변동 가능. --refresh(2단계) 또는 사용자 override 권장.',
|
|
10098
|
+
commands: [
|
|
10099
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
10100
|
+
{ cmd: '/init', desc: '워크스페이스 초기화' },
|
|
10101
|
+
{ cmd: '/model', desc: '모델 전환' },
|
|
10102
|
+
{ cmd: '/new', desc: '새 세션' },
|
|
10103
|
+
{ cmd: '/clear', desc: '컨텍스트 초기화' },
|
|
10104
|
+
{ cmd: '/status', desc: '상태 표시' }
|
|
10105
|
+
]
|
|
10106
|
+
},
|
|
10107
|
+
grok: {
|
|
10108
|
+
label: 'xAI Grok CLI', asOf: '1.9.265', invoke: 'slash',
|
|
10109
|
+
note: '커뮤니티/공식 Grok CLI 들 (예: @vibe-kit/grok-cli) 기준 큐레이션 — 배포판마다 차이 가능. 사용자 override 권장.',
|
|
10110
|
+
commands: [
|
|
10111
|
+
{ cmd: '/help', desc: '명령 목록' },
|
|
10112
|
+
{ cmd: '/clear', desc: '대화 초기화' },
|
|
10113
|
+
{ cmd: '/model', desc: '모델 전환 (grok-beta 등)' },
|
|
10114
|
+
{ cmd: '/new', desc: '새 대화' },
|
|
10115
|
+
{ cmd: '/login', desc: 'xAI API 키 로그인' },
|
|
10116
|
+
{ cmd: '/exit', desc: '종료' }
|
|
10117
|
+
]
|
|
10118
|
+
},
|
|
10119
|
+
copilot: {
|
|
10120
|
+
label: 'GitHub Copilot CLI (gh copilot)', asOf: '1.9.265', invoke: 'subcommand',
|
|
10121
|
+
note: '슬래시가 아닌 gh 하위명령. 호출 형식: gh copilot <sub> "<질의>".',
|
|
10122
|
+
commands: [
|
|
10123
|
+
{ cmd: 'suggest', desc: '명령/코드 제안 (gh copilot suggest "...")' },
|
|
10124
|
+
{ cmd: 'explain', desc: '명령 설명 (gh copilot explain "...")' },
|
|
10125
|
+
{ cmd: 'config', desc: 'gh copilot 설정' }
|
|
10126
|
+
]
|
|
10127
|
+
}
|
|
10128
|
+
};
|
|
10129
|
+
function _agentSlashFile(root) { return path.join(absRoot(root), '.harness', 'agent-slash-commands.json'); }
|
|
10130
|
+
// 사용자 override 병합 — .harness/agent-slash-commands.json 에 동일 agent id 가 있으면 commands 를 교체(최신화).
|
|
10131
|
+
// 파일 형식: { schemaVersion:1, agents: { <id>: { label?, asOf?, invoke?, note?, commands:[{cmd,desc}] } } }
|
|
10132
|
+
function _loadAgentSlashCommands(root) {
|
|
10133
|
+
const merged = {};
|
|
10134
|
+
for (const [id, def] of Object.entries(AGENT_SLASH_COMMANDS)) {
|
|
10135
|
+
merged[id] = { id, label: def.label, asOf: def.asOf, invoke: def.invoke || 'slash', note: def.note || null, source: 'builtin', commands: def.commands.slice() };
|
|
10136
|
+
}
|
|
10137
|
+
try {
|
|
10138
|
+
const f = _agentSlashFile(root);
|
|
10139
|
+
if (exists(f)) {
|
|
10140
|
+
const j = JSON.parse(read(f));
|
|
10141
|
+
const userAgents = (j && j.agents && typeof j.agents === 'object') ? j.agents : {};
|
|
10142
|
+
for (const [id, def] of Object.entries(userAgents)) {
|
|
10143
|
+
if (!def || !Array.isArray(def.commands)) continue;
|
|
10144
|
+
const cmds = def.commands.filter(c => c && typeof c.cmd === 'string').map(c => ({ cmd: c.cmd, desc: c.desc || '' }));
|
|
10145
|
+
merged[id] = {
|
|
10146
|
+
id,
|
|
10147
|
+
label: def.label || (merged[id] && merged[id].label) || id,
|
|
10148
|
+
asOf: def.asOf || (merged[id] && merged[id].asOf) || null,
|
|
10149
|
+
invoke: def.invoke || (merged[id] && merged[id].invoke) || 'slash',
|
|
10150
|
+
note: def.note || (merged[id] && merged[id].note) || null,
|
|
10151
|
+
source: merged[id] ? 'builtin+user' : 'user',
|
|
10152
|
+
commands: cmds.length ? cmds : (merged[id] ? merged[id].commands : [])
|
|
10153
|
+
};
|
|
10154
|
+
}
|
|
10155
|
+
}
|
|
10156
|
+
} catch {}
|
|
10157
|
+
return merged;
|
|
10158
|
+
}
|
|
10159
|
+
// 워크스페이스 기록 — 빌트인 레지스트리를 .harness/agent-slash-commands.json 으로 출력 (사용자가 편집해 최신화 가능).
|
|
10160
|
+
// 기존 사용자 override 가 있으면 보존(병합 결과 기록). force 시 빌트인으로 덮어쓰기.
|
|
10161
|
+
function _recordAgentSlashCommands(root, opts = {}) {
|
|
10162
|
+
const f = _agentSlashFile(root);
|
|
10163
|
+
let agents;
|
|
10164
|
+
if (opts.force || !exists(f)) {
|
|
10165
|
+
agents = {};
|
|
10166
|
+
for (const [id, def] of Object.entries(AGENT_SLASH_COMMANDS)) {
|
|
10167
|
+
agents[id] = { label: def.label, asOf: def.asOf, invoke: def.invoke || 'slash', note: def.note || undefined, commands: def.commands.slice() };
|
|
10168
|
+
}
|
|
10169
|
+
} else {
|
|
10170
|
+
const merged = _loadAgentSlashCommands(root);
|
|
10171
|
+
agents = {};
|
|
10172
|
+
for (const [id, def] of Object.entries(merged)) {
|
|
10173
|
+
agents[id] = { label: def.label, asOf: def.asOf, invoke: def.invoke, note: def.note || undefined, commands: def.commands };
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
mkdirp(path.dirname(f));
|
|
10177
|
+
writeUtf8(f, JSON.stringify({ schemaVersion: 1, recordedAt: new Date().toISOString(), agents }, null, 2) + '\n');
|
|
10178
|
+
return { file: f, agentCount: Object.keys(agents).length };
|
|
10179
|
+
}
|
|
10180
|
+
// dispatch 참조용 — 특정 agent 의 슬래시 명령어 힌트 (서브에이전트 프롬프트에 주입할 1줄 요약 + 배열).
|
|
10181
|
+
// 2단계에서 agents multi/agent dispatch 가 이 헬퍼로 각 에이전트에 맞는 슬래시 명령을 안내.
|
|
10182
|
+
function _agentSlashHint(root, agentId) {
|
|
10183
|
+
const all = _loadAgentSlashCommands(root);
|
|
10184
|
+
const def = all[agentId];
|
|
10185
|
+
if (!def) return null;
|
|
10186
|
+
const top = def.commands.slice(0, 8).map(c => c.cmd).join(' ');
|
|
10187
|
+
const invokeNote = def.invoke === 'subcommand' ? ' (슬래시 아님, 하위명령)' : '';
|
|
10188
|
+
return {
|
|
10189
|
+
agent: agentId,
|
|
10190
|
+
label: def.label,
|
|
10191
|
+
invoke: def.invoke,
|
|
10192
|
+
count: def.commands.length,
|
|
10193
|
+
commands: def.commands,
|
|
10194
|
+
summary: `${def.label}${invokeNote}: ${top}${def.commands.length > 8 ? ' …' : ''}`
|
|
10195
|
+
};
|
|
10196
|
+
}
|
|
10197
|
+
// 1.9.265 (UR-0021): leerness slash-commands [agent] [--json --record --detect]
|
|
10198
|
+
// 인자 없음 → 전체 에이전트 슬래시 명령 목록. <agent> → 단일. --record → 워크스페이스 기록. --detect → 설치된 CLI 만 표시.
|
|
10199
|
+
function slashCommandsCmd(root, agentArg, opts = {}) {
|
|
10200
|
+
const all = _loadAgentSlashCommands(root);
|
|
10201
|
+
// --detect: 실제 PATH 에 설치된 CLI 만 (EXTERNAL_AGENTS bin 기준)
|
|
10202
|
+
let installed = null;
|
|
10203
|
+
if (opts.detect) {
|
|
10204
|
+
installed = {};
|
|
10205
|
+
for (const a of EXTERNAL_AGENTS) {
|
|
10206
|
+
try { const r = cp.spawnSync(a.bin, a.versionArgs || ['--version'], { encoding: 'utf8', timeout: 4000 }); installed[a.id] = !r.error && (r.status === 0 || !!(r.stdout || '').trim()); }
|
|
10207
|
+
catch { installed[a.id] = false; }
|
|
10208
|
+
}
|
|
10209
|
+
}
|
|
10210
|
+
// --record: 워크스페이스에 기록
|
|
10211
|
+
if (opts.record) {
|
|
10212
|
+
const res = _recordAgentSlashCommands(root, { force: opts.force });
|
|
10213
|
+
if (opts.json) { log(JSON.stringify({ recorded: true, file: res.file, agentCount: res.agentCount }, null, 2)); return; }
|
|
10214
|
+
log(`✓ 슬래시 명령어 레지스트리 기록: ${res.file} (${res.agentCount} 에이전트)`);
|
|
10215
|
+
log(` → 사용자 편집으로 최신화 가능 (agents.<id>.commands 배열)`);
|
|
10216
|
+
return;
|
|
10217
|
+
}
|
|
10218
|
+
const ids = agentArg ? [agentArg] : Object.keys(all);
|
|
10219
|
+
if (agentArg && !all[agentArg]) {
|
|
10220
|
+
if (opts.json) { log(JSON.stringify({ error: `unknown agent: ${agentArg}`, known: Object.keys(all) }, null, 2)); return; }
|
|
10221
|
+
fail(`알 수 없는 에이전트: ${agentArg} (가능: ${Object.keys(all).join(', ')})`);
|
|
10222
|
+
return;
|
|
10223
|
+
}
|
|
10224
|
+
if (opts.json) {
|
|
10225
|
+
const out = { schemaVersion: 1, agents: {} };
|
|
10226
|
+
for (const id of ids) {
|
|
10227
|
+
const d = all[id];
|
|
10228
|
+
out.agents[id] = { label: d.label, asOf: d.asOf, invoke: d.invoke, note: d.note, source: d.source, count: d.commands.length, commands: d.commands };
|
|
10229
|
+
if (installed) out.agents[id].installed = !!installed[id];
|
|
10230
|
+
}
|
|
10231
|
+
log(JSON.stringify(out, null, 2));
|
|
10232
|
+
return;
|
|
10233
|
+
}
|
|
10234
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
10235
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
10236
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
10237
|
+
const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
10238
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
10239
|
+
log(cy(`# leerness slash-commands (1.9.265, UR-0021) — CLI 에이전트별 슬래시 명령어`));
|
|
10240
|
+
log(dm(` 서브에이전트 호출 시 각 에이전트에 맞는 슬래시 명령을 참조하세요. 사용자 override: ${_agentSlashFile(root)}`));
|
|
10241
|
+
log('');
|
|
10242
|
+
for (const id of ids) {
|
|
10243
|
+
const d = all[id];
|
|
10244
|
+
const instLabel = installed ? (installed[id] ? gr(' [설치됨]') : dm(' [미설치]')) : '';
|
|
10245
|
+
const srcLabel = d.source === 'builtin' ? '' : yl(` [${d.source}]`);
|
|
10246
|
+
log(`${cy('▸ ' + id)} — ${d.label}${instLabel}${srcLabel} ${dm('(asOf ' + (d.asOf || '?') + ', ' + d.commands.length + '개' + (d.invoke === 'subcommand' ? ', 하위명령' : '') + ')')}`);
|
|
10247
|
+
if (d.note) log(dm(` ⓘ ${d.note}`));
|
|
10248
|
+
for (const c of d.commands) log(` ${gr((c.cmd || '').padEnd(14))} ${dm(c.desc || '')}`);
|
|
10249
|
+
log('');
|
|
10250
|
+
}
|
|
10251
|
+
log(dm(` 기록: leerness slash-commands --record · 단일: leerness slash-commands claude · 설치 감지: --detect`));
|
|
10252
|
+
}
|
|
10253
|
+
|
|
10042
10254
|
// 1.9.157: Provider Registry — 사용자 정의 provider 동적 추가 (.harness/providers.json)
|
|
10043
10255
|
// 빌트인 5종 (EXTERNAL_AGENTS) + 사용자 정의를 merge. OpenRouter / Bedrock / Groq 등 새 CLI 즉시 흡수 가능.
|
|
10044
10256
|
// 파일 형식: { "schemaVersion": 1, "providers": [{ id, bin, envFlag, versionArgs, desc, installHint }] }
|
|
@@ -11878,6 +12090,17 @@ function sessionClose(root, opts = {}) {
|
|
|
11878
12090
|
ids: allSkills.slice(0, 10).map(s => s.id)
|
|
11879
12091
|
};
|
|
11880
12092
|
} catch {}
|
|
12093
|
+
// 1.9.264: shellGuard 통합 (session close JSON 12번째 통합 필드) — UR-0020 셸 실패 메모리 + 환경 변동
|
|
12094
|
+
try {
|
|
12095
|
+
const sf = _loadShellFailures(root);
|
|
12096
|
+
const drift = _shellEnvDrift(root);
|
|
12097
|
+
jsonResult.shellGuard = {
|
|
12098
|
+
failureCount: sf.failures.length,
|
|
12099
|
+
recent: sf.failures.slice(-3).map(f => ({ cmd: (f.cmd || '').slice(0, 50), exitCode: f.exitCode, shell: f.shell, rules: f.issues || [] })),
|
|
12100
|
+
envDriftChanges: drift && drift.changes ? drift.changes.length : 0,
|
|
12101
|
+
envDrift: drift ? drift.changes : null
|
|
12102
|
+
};
|
|
12103
|
+
} catch {}
|
|
11881
12104
|
} catch {}
|
|
11882
12105
|
try {
|
|
11883
12106
|
// 1.9.209: pre-wake-audit 자동 실행 + 저장 (sleep 전 자동 점검)
|
|
@@ -11982,6 +12205,21 @@ function sessionClose(root, opts = {}) {
|
|
|
11982
12205
|
log(grn(` ✓ 멱등성 검사 통과 — verified ${idemp.summary.verifiedAreas} 영역`));
|
|
11983
12206
|
}
|
|
11984
12207
|
} catch {}
|
|
12208
|
+
// 1.9.264: 셸 실패 메모리 + 환경 변동 요약 (UR-0020) — 마감 시 이번 세션 셸 실패를 회고에 노출
|
|
12209
|
+
try {
|
|
12210
|
+
const sf = _loadShellFailures(root);
|
|
12211
|
+
const drift = _shellEnvDrift(root);
|
|
12212
|
+
const driftN = drift && drift.changes ? drift.changes.length : 0;
|
|
12213
|
+
if (sf.failures.length > 0 || driftN > 0) {
|
|
12214
|
+
if (driftN > 0) log(yel(` ⚠ 환경 버전 변동 ${driftN}건 — 다음 세션 셸 실패 기록 재검토 권장`));
|
|
12215
|
+
if (sf.failures.length > 0) {
|
|
12216
|
+
log(yel(` 🐚 셸 실패 누적 ${sf.failures.length}건 — 다음 handoff 가 자동 노출`));
|
|
12217
|
+
log(dim(` → 명령 실행 전 점검: leerness shell-guard "<command>"`));
|
|
12218
|
+
}
|
|
12219
|
+
} else {
|
|
12220
|
+
log(grn(` ✓ 셸 실패 기록 없음 (터미널 호환성 양호)`));
|
|
12221
|
+
}
|
|
12222
|
+
} catch {}
|
|
11985
12223
|
// 1.9.237: session close --auto-cleanup-branches — 50+ release/* branches 시 자동 정리
|
|
11986
12224
|
// 1.9.224 패턴 (--auto-apply-delivered) 확장 — 마감 시 운영 누적 폐기물 자동 정리
|
|
11987
12225
|
// 안전: keep 10, merged 만, 현재 branch 보호
|
|
@@ -18688,6 +18926,17 @@ function healthCmd(root) {
|
|
|
18688
18926
|
ids: allSkills.slice(0, 10).map(s => s.id)
|
|
18689
18927
|
};
|
|
18690
18928
|
} catch { out.apiSkills = { error: 'apiSkills 점검 실패' }; }
|
|
18929
|
+
// 1.9.264: shellGuard 통합 (health JSON 12번째 통합 필드 — handoff/session close 와 JSON 3 명령 일관성) — UR-0020
|
|
18930
|
+
try {
|
|
18931
|
+
const sf = _loadShellFailures(root);
|
|
18932
|
+
const drift = _shellEnvDrift(root);
|
|
18933
|
+
out.shellGuard = {
|
|
18934
|
+
failureCount: sf.failures.length,
|
|
18935
|
+
recent: sf.failures.slice(-3).map(f => ({ cmd: (f.cmd || '').slice(0, 50), exitCode: f.exitCode, shell: f.shell, rules: f.issues || [] })),
|
|
18936
|
+
envDriftChanges: drift && drift.changes ? drift.changes.length : 0,
|
|
18937
|
+
envDrift: drift ? drift.changes : null
|
|
18938
|
+
};
|
|
18939
|
+
} catch { out.shellGuard = { error: 'shellGuard 점검 실패' }; }
|
|
18691
18940
|
// 1.9.163: 5능력 매트릭스 자동 평가 (1.9.155 sub-agent 점검 → 코드 기반 자동화)
|
|
18692
18941
|
// 각 능력을 코드 grep 으로 검출 → 0~100 점수. 사용자가 매 health 호출 시 leerness 자기 평가 확인.
|
|
18693
18942
|
try {
|
|
@@ -20148,6 +20397,11 @@ async function main() {
|
|
|
20148
20397
|
const exitRaw = arg('--exit', null);
|
|
20149
20398
|
return shellGuardCmd(arg('--path', process.cwd()), cmdArg, { record: has('--record'), exit: exitRaw != null ? parseInt(exitRaw, 10) : null, json: has('--json') });
|
|
20150
20399
|
}
|
|
20400
|
+
// 1.9.265: leerness slash-commands [agent] — CLI 에이전트별 슬래시 명령어 레지스트리 (사용자 명시 UR-0021)
|
|
20401
|
+
if (cmd === 'slash-commands' || cmd === 'slash' || cmd === 'agent-slash') {
|
|
20402
|
+
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') });
|
|
20404
|
+
}
|
|
20151
20405
|
// 1.9.245: API skill cache — 공식 문서·관련링크 자동 정리 (사용자 명시 UR-0015)
|
|
20152
20406
|
if (cmd === 'api-skill') return apiSkillCmd(arg('--path', process.cwd()), args[1] || 'help');
|
|
20153
20407
|
// 1.9.208: leerness constraints <list|check|add> — 플랫폼/API 제약 사전 체크 (사용자 명시)
|
|
@@ -20313,5 +20567,7 @@ module.exports = {
|
|
|
20313
20567
|
// 1.9.260: shell-guard — 셸 호환성 린터 (UR-0020) 순수 분석 함수 — 단위 테스트
|
|
20314
20568
|
_shellGuardAnalyze, _detectShellCtx, shellGuardCmd,
|
|
20315
20569
|
// 1.9.263: shell 실패 메모리 + 환경 버전 변동 (UR-0020 3단계) — handoff 통합 단위 테스트
|
|
20316
|
-
_shellFailuresPath, _loadShellFailures, _recordShellFailure, _shellEnvDrift
|
|
20570
|
+
_shellFailuresPath, _loadShellFailures, _recordShellFailure, _shellEnvDrift,
|
|
20571
|
+
// 1.9.265: CLI 에이전트 슬래시 명령어 레지스트리 (UR-0021 1단계) — 단위 테스트
|
|
20572
|
+
AGENT_SLASH_COMMANDS, _agentSlashFile, _loadAgentSlashCommands, _recordAgentSlashCommands, _agentSlashHint, slashCommandsCmd
|
|
20317
20573
|
};
|