leerness 1.9.376 → 1.9.378
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 +29 -0
- package/README.md +6 -6
- package/bin/harness.js +15 -61
- package/lib/mcp-tools.js +2 -0
- package/lib/pure-utils.js +64 -1
- package/package.json +1 -1
- package/scripts/e2e.js +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.378 — 2026-06-06 — UR-0073: 에이전트 팀 MCP 노출 (read-only list/preview)
|
|
4
|
+
|
|
5
|
+
**🧩 신규 team 서브시스템을 MCP 로 외부 AI 에 노출 — 에이전트 팀 비전을 외부 에이전트가 직접 활용.**
|
|
6
|
+
|
|
7
|
+
### 배경
|
|
8
|
+
leerness 는 MCP-first(80+ 도구)이나 신규 team 서브시스템(UR-0073)이 MCP 미노출이었음. 외부 AI 에이전트가 팀을 조회/미리보기할 수 있어야 비전("에이전트가 팀 관리/실행")이 완성. **읽기 전용만 노출**(deploy 는 CLI 게이트 유지 — MCP 로 배포 트리거 방지).
|
|
9
|
+
|
|
10
|
+
### 구현
|
|
11
|
+
1. **`leerness_team_list`**(read-only): 정의된 팀 목록 JSON.
|
|
12
|
+
2. **`leerness_team_preview`**(read-only, `id` required, `task?`): 팀 실행 계획 dry-run 미리보기 JSON(실행 없음).
|
|
13
|
+
3. harness MCP dispatch 2 case(`team list/preview --json` 매핑). MCP 도구 83→85(`_mcpToolCount` 자동 반영).
|
|
14
|
+
|
|
15
|
+
### 검증 (회귀 0)
|
|
16
|
+
- **selftest 123→124 PASS** (MCP 정의 read-only + required id + dispatch 와이어). **E2E 322→323 PASS** (정의 + 매핑 CLI 동작).
|
|
17
|
+
- 실측: mcp-tools 85개, team_list/preview read-only, 매핑 CLI 정상.
|
|
18
|
+
|
|
19
|
+
## 1.9.377 — 2026-06-06 — UR-0025 모듈화: workspace reference guide 빌더 분리
|
|
20
|
+
|
|
21
|
+
**🧩 워크스페이스 레퍼런스 가이드 빌더(~57줄)를 lib/pure-utils 로 분리.**
|
|
22
|
+
|
|
23
|
+
### 구현
|
|
24
|
+
1. **`_renderWorkspaceReferenceGuide(dirName, version, generatedAt)`**(pure-utils): 디렉토리 구조/자주 묻는 위치/마이그레이션 안내 가이드 빌더. version·timestamp 주입(순수).
|
|
25
|
+
2. **harness `_buildWorkspaceReferenceGuide(root, dirName)`**: 얇은 래퍼(`_renderWorkspaceReferenceGuide(dirName, VERSION, new Date().toISOString())`).
|
|
26
|
+
3. 전사 위험 회피: node 스크립트로 결정적 교체 + 출력 동일성(핵심 마커) 검증.
|
|
27
|
+
|
|
28
|
+
### 검증 (회귀 0)
|
|
29
|
+
- **selftest 122→123 PASS** (행위: 빌더가 dirName/version 반영 + 핵심 섹션 포함 + 래퍼 얇음 + 모듈 reference equality).
|
|
30
|
+
- **E2E 321→322 PASS** (행위: lib/pure-utils 직접 호출로 가이드 빌더 출력 검증).
|
|
31
|
+
|
|
3
32
|
## 1.9.376 — 2026-06-06 — UR-0073 Phase D: team deploy 2중 게이트 (에이전트 팀 umbrella 완결) 🎉
|
|
4
33
|
|
|
5
34
|
**🧩 정의된 팀의 사용자 설정 배포 명령을 안전 게이트로 실행 — `leerness team deploy`. UR-0073 A~D 완결.**
|
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.378 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
475
475
|
|
|
476
476
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
477
477
|
|
|
@@ -525,7 +525,7 @@ leerness memory restore decision <date|title>
|
|
|
525
525
|
|
|
526
526
|
### MCP server (외부 AI 통합)
|
|
527
527
|
|
|
528
|
-
Leerness v1.9.
|
|
528
|
+
Leerness v1.9.378는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
529
529
|
|
|
530
530
|
```jsonc
|
|
531
531
|
// 카테고리별
|
|
@@ -538,7 +538,7 @@ Leerness v1.9.376는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
538
538
|
// • Workflow: session_close / agents_list / task_export / env_check / usage_stats / reuse_map / whats_new
|
|
539
539
|
|
|
540
540
|
// MCP server 실행: leerness mcp serve
|
|
541
|
-
// tools/list 응답:
|
|
541
|
+
// tools/list 응답: 85 도구
|
|
542
542
|
```
|
|
543
543
|
|
|
544
544
|
### Autonomous mode (자율 모드)
|
|
@@ -546,7 +546,7 @@ Leerness v1.9.376는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
546
546
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
547
547
|
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) 다음 라운드 예약.
|
|
548
548
|
|
|
549
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
549
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.378)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
550
550
|
|
|
551
551
|
### 성능 가이드 (1.9.140 측정)
|
|
552
552
|
|
|
@@ -584,6 +584,6 @@ leerness release pack --close --auto-main-push
|
|
|
584
584
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
585
585
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
586
586
|
|
|
587
|
-
Last synced by Leerness v1.9.
|
|
587
|
+
Last synced by Leerness v1.9.378: 2026-06-06
|
|
588
588
|
<!-- leerness:project-readme:end -->
|
|
589
589
|
|
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
|
// 1.9.274 (UR-0025 1단계): 순수 유틸 함수 모듈 분리 (require-based, 비파괴). selftest 7종이 동작 검증.
|
|
10
|
-
const { _isSecretKey, _isPlaceholderSecret, _looksSecretLike, _mergeLines, _mergeEnvLines, _mergeReadmeSection, _managedMerge, _parseSkillsValue, _parseArchiveBlocks, _parseSkillCatalog, _renderTeamsMd, _composeTeamPlan, _teamHandoffReminders, _cadenceAssessment, _teamDeployGate, compareVer, parseHarnessVersion, _classifyCJK, _riskLabel, _detectSystemLang, _parseSlashFromHelp,
|
|
10
|
+
const { _isSecretKey, _isPlaceholderSecret, _looksSecretLike, _mergeLines, _mergeEnvLines, _mergeReadmeSection, _managedMerge, _parseSkillsValue, _parseArchiveBlocks, _parseSkillCatalog, _renderTeamsMd, _composeTeamPlan, _teamHandoffReminders, _cadenceAssessment, _teamDeployGate, _renderWorkspaceReferenceGuide, compareVer, parseHarnessVersion, _classifyCJK, _riskLabel, _detectSystemLang, _parseSlashFromHelp,
|
|
11
11
|
PERMISSION_TIERS, _tierRank, _requiredTier, _policyAllows, _resolveNpmTag, _mcpJsonContent, _newRunRecord,
|
|
12
12
|
_htmlToText, _extractTitle, _extractLinks,
|
|
13
13
|
_countDatedBlocks, _extractDecisionBlocks, _classifyIntent,
|
|
@@ -28,7 +28,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
28
28
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
29
29
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _LSP_LANG_PATTERNS, OPTIMISM_PATTERNS, BUILT_IN_PERSONAS, STRINGS, BUILTIN_CATALOG, ROADMAP_STATUS_LABEL, ROADMAP_STATUS_COLOR, SECRET_PATTERNS, MERGE_OVERWRITE_FILES, MINIMAL_SKIP_KEYS, SKILL_CATALOG_PRESETS } = require('../lib/catalogs'); // 1.9.344/368/369 (UR-0025): catalog 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
|
|
30
30
|
|
|
31
|
-
const VERSION = '1.9.
|
|
31
|
+
const VERSION = '1.9.378';
|
|
32
32
|
|
|
33
33
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
34
34
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3013,6 +3013,8 @@ function _selfTestCases() {
|
|
|
3013
3013
|
{ name: 'UR-0074: _cadenceAssessment 릴리스 빈도 평가 (임계값) 행위 (1.9.374)', run: () => { const m = require('../lib/pure-utils'); if (typeof _cadenceAssessment !== 'function' || m._cadenceAssessment !== _cadenceAssessment || typeof releaseCadenceCmd !== 'function') return false; return _cadenceAssessment(7, 1, 1).level === 'very-high' && _cadenceAssessment(3, 1, 1).level === 'high' && _cadenceAssessment(1, 1, 1).level === 'moderate' && _cadenceAssessment(0.2, 1, 1).level === 'healthy' && _cadenceAssessment(7, 1, 1).recommendation.length > 0; } },
|
|
3014
3014
|
{ name: 'UR-0084: _withLock 획득/재진입/해제 + maxWaitMs 하드닝(10s) 행위 (1.9.375)', run: () => { if (typeof _withLock !== 'function') return false; const src = read(__filename); const hardened = /maxWaitMs = opts\.maxWaitMs \|\| 10000/.test(src); const tmp = fs.mkdtempSync(path.join(os.tmpdir(), '__leerness_lock_')); try { const target = path.join(tmp, 'f.md'); let reentrant = false; const lockSeen = _withLock(target, () => { const exists = fs.existsSync(target + '.lock'); reentrant = (_withLock(target, () => 42) === 42); return exists; }); const cleaned = !fs.existsSync(target + '.lock'); return hardened && lockSeen === true && reentrant === true && cleaned; } finally { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} } } },
|
|
3015
3015
|
{ name: 'UR-0073 Phase D: _teamDeployGate 이중 게이트 (dry-run 기본/env 게이트/실행) 행위 (1.9.376)', run: () => { const m = require('../lib/pure-utils'); if (typeof _teamDeployGate !== 'function' || m._teamDeployGate !== _teamDeployGate) return false; const team = { id: 'd', deployCommand: 'echo hi' }; const noCmd = _teamDeployGate({ id: 'x' }, { yes: true, envOn: true }).mode === 'no-command'; const dry = _teamDeployGate(team, { yes: false, envOn: true }).mode === 'dry-run'; const gated = _teamDeployGate(team, { yes: true, envOn: false }).mode === 'gated'; const exec = _teamDeployGate(team, { yes: true, envOn: true }).mode === 'execute'; return noCmd && dry && gated && exec; } },
|
|
3016
|
+
{ name: 'UR-0025: _renderWorkspaceReferenceGuide 모듈 분리 + 빌더 동작 (1.9.377)', run: () => { const m = require('../lib/pure-utils'); if (typeof _renderWorkspaceReferenceGuide !== 'function' || m._renderWorkspaceReferenceGuide !== _renderWorkspaceReferenceGuide) return false; const g = _renderWorkspaceReferenceGuide('.leerness', '9.9.9', '2026-01-01T00:00:00.000Z'); const wrapperThin = read(__filename).includes('return _renderWorkspaceReferenceGuide(dirName, VERSION, new Date().toISOString())'); return g.includes('.leerness/progress-tracker.md') && g.includes('9.9.9') && g.includes('자주 묻는 위치') && g.includes('마이그레이션 안내') && wrapperThin; } },
|
|
3017
|
+
{ name: 'UR-0073: team MCP 도구 2종(read-only) 정의 + dispatch 와이어 (1.9.378)', run: () => { const tools = require('../lib/mcp-tools'); const src = read(__filename); const tl = tools.find(t => t.name === 'leerness_team_list'); const tp = tools.find(t => t.name === 'leerness_team_preview'); const defsOk = tl && tl.requiredTier === 'read-only' && tp && tp.requiredTier === 'read-only' && tp.inputSchema.required && tp.inputSchema.required.includes('id'); const wired = src.includes("case " + "'leerness_team_list':") && src.includes("case " + "'leerness_team_preview':") && /cliArgs = \['team', 'list'/.test(src) && /cliArgs = \['team', 'preview'/.test(src); return !!defsOk && wired; } },
|
|
3016
3018
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3017
3019
|
];
|
|
3018
3020
|
}
|
|
@@ -4235,66 +4237,9 @@ function _migrateWorkspaceDir(root, opts = {}) {
|
|
|
4235
4237
|
return report;
|
|
4236
4238
|
}
|
|
4237
4239
|
// AI 참조 가이드 빌더 — 워크스페이스 디렉토리 구조 + 파일별 역할 매핑
|
|
4240
|
+
// 1.9.377 (UR-0025): 순수 코어 _renderWorkspaceReferenceGuide (lib/pure-utils) + 얇은 래퍼 (root 미사용, version/timestamp 주입).
|
|
4238
4241
|
function _buildWorkspaceReferenceGuide(root, dirName) {
|
|
4239
|
-
|
|
4240
|
-
lines.push(`# Leerness Workspace Reference Guide`);
|
|
4241
|
-
lines.push('');
|
|
4242
|
-
lines.push(`> AI 에이전트가 leerness 워크스페이스에서 어떤 파일을 어디서 찾는지 안내합니다 (1.9.211).`);
|
|
4243
|
-
lines.push('');
|
|
4244
|
-
lines.push(`Generated: ${new Date().toISOString()} by leerness ${VERSION}`);
|
|
4245
|
-
lines.push(`Workspace dir: \`${dirName}/\``);
|
|
4246
|
-
lines.push('');
|
|
4247
|
-
lines.push(`## 📁 디렉토리 구조 (핵심)`);
|
|
4248
|
-
lines.push('');
|
|
4249
|
-
lines.push('```');
|
|
4250
|
-
lines.push(`${dirName}/`);
|
|
4251
|
-
lines.push(`├── plan.md ← 무엇을 할 것인가 (사용자 메모리)`);
|
|
4252
|
-
lines.push(`├── progress-tracker.md ← 무엇을 했는가 (증거 포함, 사용자 메모리)`);
|
|
4253
|
-
lines.push(`├── decisions.md ← 왜 그렇게 했는가 (사용자 메모리)`);
|
|
4254
|
-
lines.push(`├── session-handoff.md ← 다음 세션 인계 (사용자 메모리)`);
|
|
4255
|
-
lines.push(`├── lessons.md ← 과거 교훈 (자동 fuzzy 회수)`);
|
|
4256
|
-
lines.push(`├── rules.md ← 자연어 룰 (매 세션 자동 노출, R-XXXX)`);
|
|
4257
|
-
lines.push(`├── task-log.md ← in-progress / dropped task 이력`);
|
|
4258
|
-
lines.push(`├── reuse-map.md ← 워크스페이스 capability 매핑`);
|
|
4259
|
-
lines.push(`├── skill-suggestions.md ← skill rolling history`);
|
|
4260
|
-
lines.push(`├── feature-graph.md ← 기능 의존 그래프 (F-XXXX)`);
|
|
4261
|
-
lines.push(`├── manifest.json ← 워크스페이스 메타`);
|
|
4262
|
-
lines.push(`├── leerness-config.json ← 비시크릿 LEERNESS_* 설정 (1.9.187, AI 가시)`);
|
|
4263
|
-
lines.push(`├── user-requests.json ← 사용자 명시 요청 누적 (1.9.207)`);
|
|
4264
|
-
lines.push(`├── active-wakeups.json ← ScheduleWakeup 상태 (1.9.205)`);
|
|
4265
|
-
lines.push(`├── pre-wake-report.json ← sleep 전 sub-agent audit (1.9.209)`);
|
|
4266
|
-
lines.push(`├── wakeup-history.json ← adaptive wakeup 이력 (1.9.210)`);
|
|
4267
|
-
lines.push(`├── platform-constraints.json ← API 제약 catalog (1.9.208)`);
|
|
4268
|
-
lines.push(`├── auto-resume-plan.json ← 다음 라운드 plan (1.9.203)`);
|
|
4269
|
-
lines.push(`├── next-action-queue.json ← 다음 next-action 큐 (1.9.201)`);
|
|
4270
|
-
lines.push(`├── last-handoff.json ← 마지막 handoff timestamp`);
|
|
4271
|
-
lines.push(`├── environment.json ← 환경 변동 추적 (1.9.145)`);
|
|
4272
|
-
lines.push(`├── skills/ ← 설치된 skill 디렉토리`);
|
|
4273
|
-
lines.push(`└── templates/ ← 워크스페이스 템플릿`);
|
|
4274
|
-
lines.push('```');
|
|
4275
|
-
lines.push('');
|
|
4276
|
-
lines.push(`## 🧭 자주 묻는 위치`);
|
|
4277
|
-
lines.push('');
|
|
4278
|
-
lines.push(`| 찾는 것 | 위치 |`);
|
|
4279
|
-
lines.push(`|---|---|`);
|
|
4280
|
-
lines.push(`| 현재 진행 중인 task | \`${dirName}/progress-tracker.md\` (status: in-progress) |`);
|
|
4281
|
-
lines.push(`| 사용자가 명시한 영구 룰 | \`${dirName}/rules.md\` (active R-XXXX) |`);
|
|
4282
|
-
lines.push(`| 직전 sleep 전 audit 결과 | \`${dirName}/pre-wake-report.json\` (1.9.209) |`);
|
|
4283
|
-
lines.push(`| 미답 사용자 요청 | \`${dirName}/user-requests.json\` (status: open) |`);
|
|
4284
|
-
lines.push(`| 다음 라운드 권장 단계 | \`${dirName}/auto-resume-plan.json\` (1.9.203) |`);
|
|
4285
|
-
lines.push(`| API 제약 catalog | \`${dirName}/platform-constraints.json\` (1.9.208) |`);
|
|
4286
|
-
lines.push(`| 자동 wakeup 권장 간격 | \`${dirName}/wakeup-history.json\` (1.9.210) |`);
|
|
4287
|
-
lines.push('');
|
|
4288
|
-
lines.push(`## 🔄 마이그레이션 안내`);
|
|
4289
|
-
lines.push('');
|
|
4290
|
-
lines.push(`이 워크스페이스는 \`.harness\` → \`.leerness\` 로 마이그레이션되었을 수 있습니다.`);
|
|
4291
|
-
lines.push(`- \`.leerness/MIGRATED_FROM_HARNESS\` 존재 → 마이그레이션 완료, \`.leerness\` 우선 사용`);
|
|
4292
|
-
lines.push(`- \`.harness/MIGRATED_TO_LEERNESS.md\` 존재 → \`.leerness/\` 로 가야 함`);
|
|
4293
|
-
lines.push(`- 양쪽 모두 없음 → 기본 \`.harness\` 사용 중`);
|
|
4294
|
-
lines.push('');
|
|
4295
|
-
lines.push(`AI 에이전트는 \`leerness handoff .\` 결과를 신뢰하십시오 — 자동으로 올바른 디렉토리를 사용합니다.`);
|
|
4296
|
-
lines.push('');
|
|
4297
|
-
return lines.join('\n');
|
|
4242
|
+
return _renderWorkspaceReferenceGuide(dirName, VERSION, new Date().toISOString());
|
|
4298
4243
|
}
|
|
4299
4244
|
|
|
4300
4245
|
// 1.9.213: intent inference + scope expansion 게이트 (사용자 명시)
|
|
@@ -16732,6 +16677,15 @@ function _mcpToCliArgs(name, args, targetPath) {
|
|
|
16732
16677
|
// 1.9.233: 카테고리화된 전체 CLI 명령 목록
|
|
16733
16678
|
cliArgs = ['commands', '--json'];
|
|
16734
16679
|
break;
|
|
16680
|
+
case 'leerness_team_list':
|
|
16681
|
+
// 1.9.378 (UR-0073): 정의된 에이전트 팀 목록 (read-only)
|
|
16682
|
+
cliArgs = ['team', 'list', '--path', targetPath, '--json'];
|
|
16683
|
+
break;
|
|
16684
|
+
case 'leerness_team_preview':
|
|
16685
|
+
// 1.9.378 (UR-0073 Phase B): 팀 실행 계획 dry-run 미리보기 (실행 없음)
|
|
16686
|
+
cliArgs = ['team', 'preview', String(args.id || ''), '--path', targetPath, '--json'];
|
|
16687
|
+
if (args.task) cliArgs.push('--task', String(args.task));
|
|
16688
|
+
break;
|
|
16735
16689
|
case 'leerness_py_check':
|
|
16736
16690
|
// 1.9.239 (UR-0013): Python 파일 분석
|
|
16737
16691
|
cliArgs = ['py-check', '--path', targetPath, '--json'];
|
package/lib/mcp-tools.js
CHANGED
|
@@ -69,6 +69,8 @@ module.exports = [
|
|
|
69
69
|
{ name: 'leerness_milestones', requiredTier: 'read-only', description: '1.9.229 (1.9.226 확장) — 도달 마일스톤 + 다음 ETA. git tag 순차 분석으로 25/50/75/100/125/150/175/200/250/300/400/500 마일스톤 도달 일자 + 다음 마일스톤 ETA (현재 속도 기준) 계산. 응답: { totalRounds, reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, etaDays, etaDate}, baselineAt, avgRoundsPerDay }. 외부 AI가 "지금까지 어떤 마일스톤을 언제 달성했고 다음 마일스톤 예상 도달일은?"을 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
70
70
|
{ name: 'leerness_pulse', requiredTier: 'read-only', description: '1.9.231 — 한 줄 종합 요약 (10 핵심 지표). 응답: { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormalShutdown }. 외부 AI가 "leerness 상태 한 눈에 보기"를 가벼운 단일 호출로 회수. handoff 보다 5배 빠름 (drift/health 계산 skip). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
71
71
|
{ name: 'leerness_commands', requiredTier: 'read-only', description: '1.9.233 — 카테고리화된 전체 CLI 명령 목록 (9 카테고리). 응답: { version, totalCommands, categories: { status, task, memory, audit, workflow, release, skill, bridge, config } }. 외부 AI가 "leerness 가 제공하는 명령이 뭐가 있나"를 직접 회수. 매뉴얼/도움말 동적 생성. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
72
|
+
{ name: 'leerness_team_list', requiredTier: 'read-only', description: '1.9.378 (UR-0073) — 정의된 에이전트 팀 목록 (페르소나 기반, opt-in · 정의 전용). 응답: { version, count, teams: [{ id, name, purpose, personas[], members[], schedule, deployCommand, status }] }. 외부 AI 가 "이 워크스페이스에 정의된 팀이 뭐가 있나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
73
|
+
{ name: 'leerness_team_preview', requiredTier: 'read-only', description: '1.9.378 (UR-0073 Phase B) — 팀 실행 계획 dry-run 미리보기 (실제 실행/배포 없음). 응답: { dryRun: true, teamId, task, schedule, memberCount, steps: [{ member, personas[], dispatchPrompt, suggestedCommand }] }. 외부 AI 가 "이 팀이 무엇을 할지"를 안전하게 회수. 인자: { id (required), task?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, task: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
72
74
|
{ name: 'leerness_release_cleanup', requiredTier: 'git-write', description: '1.9.236 (1.9.235 자동 회수) — local release/* branches 정리. main 에 merge된 것만 후보 (unmerged 보호, 현재 branch 보호). 응답: { apply, keep, total, merged, unmerged, deleteCount, toDelete[], recent[], unmergedSample[] }. 외부 AI가 "운영 누적 release branches 정리 가능?"을 회수. 기본 dry-run, apply: true 시에만 실 삭제. 인자: { path?, apply? (default false), keep? (default 5) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' }, keep: { type: 'number' } } } },
|
|
73
75
|
{ name: 'leerness_py_check', requiredTier: 'read-only', description: '1.9.239 (사용자 명시 UR-0013) — Python 파일 분석. 의존성 0 regex fallback. .md 외 .py 도 leerness 인지. 응답: { totalFiles, totalLOC, totalImports, totalFuncs, totalClasses, totalTodos, biggest: [{ file, loc, funcs, classes }] }. 외부 AI가 "이 프로젝트 Python 표면이 얼마나 되나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
74
76
|
{ name: 'leerness_agent_mode', requiredTier: 'safe-write', description: '1.9.239 (사용자 명시 UR-0013) — 자율 모드 전용 통합 명령. start: handoff + drift --auto-fix + session-resume --auto-fix (진입). tick: pulse 한 줄 (매 라운드). stop: session close --auto-apply-delivered --auto-cleanup-branches (마감). 외부 AI 가 자율 라운드 진입/매 라운드/마감을 단일 호출로 수행. 인자: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } },
|
package/lib/pure-utils.js
CHANGED
|
@@ -792,11 +792,74 @@ function _teamDeployGate(team, opts) {
|
|
|
792
792
|
return { mode: 'execute', command, message: '실행 허용 (--yes + env 게이트 충족)' };
|
|
793
793
|
}
|
|
794
794
|
|
|
795
|
+
// 1.9.377 (UR-0025): 워크스페이스 레퍼런스 가이드 빌더 (순수) — dirName/version/generatedAt 주입. harness 인라인(~57줄) 분리.
|
|
796
|
+
function _renderWorkspaceReferenceGuide(dirName, version, generatedAt) {
|
|
797
|
+
const lines = [];
|
|
798
|
+
lines.push(`# Leerness Workspace Reference Guide`);
|
|
799
|
+
lines.push('');
|
|
800
|
+
lines.push(`> AI 에이전트가 leerness 워크스페이스에서 어떤 파일을 어디서 찾는지 안내합니다 (1.9.211).`);
|
|
801
|
+
lines.push('');
|
|
802
|
+
lines.push(`Generated: ${generatedAt} by leerness ${version}`);
|
|
803
|
+
lines.push(`Workspace dir: \`${dirName}/\``);
|
|
804
|
+
lines.push('');
|
|
805
|
+
lines.push(`## 📁 디렉토리 구조 (핵심)`);
|
|
806
|
+
lines.push('');
|
|
807
|
+
lines.push('```');
|
|
808
|
+
lines.push(`${dirName}/`);
|
|
809
|
+
lines.push(`├── plan.md ← 무엇을 할 것인가 (사용자 메모리)`);
|
|
810
|
+
lines.push(`├── progress-tracker.md ← 무엇을 했는가 (증거 포함, 사용자 메모리)`);
|
|
811
|
+
lines.push(`├── decisions.md ← 왜 그렇게 했는가 (사용자 메모리)`);
|
|
812
|
+
lines.push(`├── session-handoff.md ← 다음 세션 인계 (사용자 메모리)`);
|
|
813
|
+
lines.push(`├── lessons.md ← 과거 교훈 (자동 fuzzy 회수)`);
|
|
814
|
+
lines.push(`├── rules.md ← 자연어 룰 (매 세션 자동 노출, R-XXXX)`);
|
|
815
|
+
lines.push(`├── task-log.md ← in-progress / dropped task 이력`);
|
|
816
|
+
lines.push(`├── reuse-map.md ← 워크스페이스 capability 매핑`);
|
|
817
|
+
lines.push(`├── skill-suggestions.md ← skill rolling history`);
|
|
818
|
+
lines.push(`├── feature-graph.md ← 기능 의존 그래프 (F-XXXX)`);
|
|
819
|
+
lines.push(`├── manifest.json ← 워크스페이스 메타`);
|
|
820
|
+
lines.push(`├── leerness-config.json ← 비시크릿 LEERNESS_* 설정 (1.9.187, AI 가시)`);
|
|
821
|
+
lines.push(`├── user-requests.json ← 사용자 명시 요청 누적 (1.9.207)`);
|
|
822
|
+
lines.push(`├── active-wakeups.json ← ScheduleWakeup 상태 (1.9.205)`);
|
|
823
|
+
lines.push(`├── pre-wake-report.json ← sleep 전 sub-agent audit (1.9.209)`);
|
|
824
|
+
lines.push(`├── wakeup-history.json ← adaptive wakeup 이력 (1.9.210)`);
|
|
825
|
+
lines.push(`├── platform-constraints.json ← API 제약 catalog (1.9.208)`);
|
|
826
|
+
lines.push(`├── auto-resume-plan.json ← 다음 라운드 plan (1.9.203)`);
|
|
827
|
+
lines.push(`├── next-action-queue.json ← 다음 next-action 큐 (1.9.201)`);
|
|
828
|
+
lines.push(`├── last-handoff.json ← 마지막 handoff timestamp`);
|
|
829
|
+
lines.push(`├── environment.json ← 환경 변동 추적 (1.9.145)`);
|
|
830
|
+
lines.push(`├── skills/ ← 설치된 skill 디렉토리`);
|
|
831
|
+
lines.push(`└── templates/ ← 워크스페이스 템플릿`);
|
|
832
|
+
lines.push('```');
|
|
833
|
+
lines.push('');
|
|
834
|
+
lines.push(`## 🧭 자주 묻는 위치`);
|
|
835
|
+
lines.push('');
|
|
836
|
+
lines.push(`| 찾는 것 | 위치 |`);
|
|
837
|
+
lines.push(`|---|---|`);
|
|
838
|
+
lines.push(`| 현재 진행 중인 task | \`${dirName}/progress-tracker.md\` (status: in-progress) |`);
|
|
839
|
+
lines.push(`| 사용자가 명시한 영구 룰 | \`${dirName}/rules.md\` (active R-XXXX) |`);
|
|
840
|
+
lines.push(`| 직전 sleep 전 audit 결과 | \`${dirName}/pre-wake-report.json\` (1.9.209) |`);
|
|
841
|
+
lines.push(`| 미답 사용자 요청 | \`${dirName}/user-requests.json\` (status: open) |`);
|
|
842
|
+
lines.push(`| 다음 라운드 권장 단계 | \`${dirName}/auto-resume-plan.json\` (1.9.203) |`);
|
|
843
|
+
lines.push(`| API 제약 catalog | \`${dirName}/platform-constraints.json\` (1.9.208) |`);
|
|
844
|
+
lines.push(`| 자동 wakeup 권장 간격 | \`${dirName}/wakeup-history.json\` (1.9.210) |`);
|
|
845
|
+
lines.push('');
|
|
846
|
+
lines.push(`## 🔄 마이그레이션 안내`);
|
|
847
|
+
lines.push('');
|
|
848
|
+
lines.push(`이 워크스페이스는 \`.harness\` → \`.leerness\` 로 마이그레이션되었을 수 있습니다.`);
|
|
849
|
+
lines.push(`- \`.leerness/MIGRATED_FROM_HARNESS\` 존재 → 마이그레이션 완료, \`.leerness\` 우선 사용`);
|
|
850
|
+
lines.push(`- \`.harness/MIGRATED_TO_LEERNESS.md\` 존재 → \`.leerness/\` 로 가야 함`);
|
|
851
|
+
lines.push(`- 양쪽 모두 없음 → 기본 \`.harness\` 사용 중`);
|
|
852
|
+
lines.push('');
|
|
853
|
+
lines.push(`AI 에이전트는 \`leerness handoff .\` 결과를 신뢰하십시오 — 자동으로 올바른 디렉토리를 사용합니다.`);
|
|
854
|
+
lines.push('');
|
|
855
|
+
return lines.join('\n');
|
|
856
|
+
}
|
|
857
|
+
|
|
795
858
|
module.exports = {
|
|
796
859
|
_isSecretKey, compareVer, parseHarnessVersion,
|
|
797
860
|
_isPlaceholderSecret, _looksSecretLike,
|
|
798
861
|
_mergeLines, _mergeEnvLines, _mergeReadmeSection, _managedMerge, _parseSkillsValue,
|
|
799
|
-
_parseArchiveBlocks, _parseSkillCatalog, _renderTeamsMd, _composeTeamPlan, _teamHandoffReminders, _cadenceAssessment, _teamDeployGate,
|
|
862
|
+
_parseArchiveBlocks, _parseSkillCatalog, _renderTeamsMd, _composeTeamPlan, _teamHandoffReminders, _cadenceAssessment, _teamDeployGate, _renderWorkspaceReferenceGuide,
|
|
800
863
|
_classifyCJK, _riskLabel, _detectSystemLang, _parseSlashFromHelp,
|
|
801
864
|
// 1.9.283 (UR-0025 2단계)
|
|
802
865
|
PERMISSION_TIERS, _tierRank, _requiredTier, _policyAllows, _resolveNpmTag, _mcpJsonContent, _newRunRecord,
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -5195,5 +5195,42 @@ total++;
|
|
|
5195
5195
|
if (!ok) failed++;
|
|
5196
5196
|
}
|
|
5197
5197
|
|
|
5198
|
+
// 1.9.377 회귀 (UR-0025): _renderWorkspaceReferenceGuide 모듈 분리 — 빌더가 dirName/version 반영 + 핵심 섹션 포함 (출력 동일성)
|
|
5199
|
+
total++;
|
|
5200
|
+
{
|
|
5201
|
+
let ok = false;
|
|
5202
|
+
try {
|
|
5203
|
+
const pu = require(path.resolve(__dirname, '..', 'lib', 'pure-utils'));
|
|
5204
|
+
const g = pu._renderWorkspaceReferenceGuide('.harness', '1.2.3', '2026-01-01T00:00:00.000Z');
|
|
5205
|
+
ok = typeof g === 'string' && g.includes('.harness/progress-tracker.md') && g.includes('by leerness 1.2.3')
|
|
5206
|
+
&& g.includes('## 📁 디렉토리 구조 (핵심)') && g.includes('## 🧭 자주 묻는 위치') && g.includes('## 🔄 마이그레이션 안내') && g.includes('plan.md');
|
|
5207
|
+
} catch {}
|
|
5208
|
+
console.log(ok ? '✓ B(1.9.377) UR-0025: _renderWorkspaceReferenceGuide 모듈 분리 (빌더 동작 + 핵심 섹션)' : '✗ workspace guide 빌더 실패');
|
|
5209
|
+
if (!ok) failed++;
|
|
5210
|
+
}
|
|
5211
|
+
|
|
5212
|
+
// 1.9.378 회귀 (UR-0073): team MCP 도구 2종(read-only) 정의 + 매핑 CLI(team list/preview) 동작
|
|
5213
|
+
total++;
|
|
5214
|
+
{
|
|
5215
|
+
let ok = false;
|
|
5216
|
+
try {
|
|
5217
|
+
const tools = require(path.resolve(__dirname, '..', 'lib', 'mcp-tools'));
|
|
5218
|
+
const tl = tools.find(t => t.name === 'leerness_team_list');
|
|
5219
|
+
const tp = tools.find(t => t.name === 'leerness_team_preview');
|
|
5220
|
+
const defsOk = !!tl && tl.requiredTier === 'read-only' && !!tp && tp.requiredTier === 'read-only' && tp.inputSchema.required.includes('id') && tools.length >= 85;
|
|
5221
|
+
// 매핑 대상 CLI 동작 (MCP dispatch 가 호출하는 것)
|
|
5222
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tmcp-'));
|
|
5223
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
5224
|
+
cp.spawnSync(process.execPath, [CLI, 'team', 'add', 'rev', '--members', 'claude', '--path', d], { encoding: 'utf8', timeout: 15000 });
|
|
5225
|
+
const lj = cp.spawnSync(process.execPath, [CLI, 'team', 'list', '--path', d, '--json'], { encoding: 'utf8', timeout: 15000 });
|
|
5226
|
+
let cliOk = false;
|
|
5227
|
+
try { cliOk = JSON.parse(lj.stdout).count === 1; } catch {}
|
|
5228
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
5229
|
+
ok = defsOk && cliOk;
|
|
5230
|
+
} catch {}
|
|
5231
|
+
console.log(ok ? '✓ B(1.9.378) UR-0073: team MCP 도구(read-only list/preview) 정의 + 매핑 CLI 동작' : '✗ team MCP 도구 실패');
|
|
5232
|
+
if (!ok) failed++;
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5198
5235
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
5199
5236
|
if (failed > 0) process.exit(1);
|