ltcai 0.1.9 → 0.1.16

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.
Files changed (43) hide show
  1. package/README.md +174 -305
  2. package/docs/CHANGELOG.md +307 -0
  3. package/docs/architecture.md +121 -0
  4. package/docs/mcp-tools.md +116 -0
  5. package/docs/privacy.md +74 -0
  6. package/docs/public-deploy.md +137 -0
  7. package/docs/security-model.md +121 -0
  8. package/knowledge_graph.py +123 -15
  9. package/llm_router.py +100 -28
  10. package/ltcai_cli.py +138 -5
  11. package/package.json +14 -2
  12. package/server.py +1756 -329
  13. package/skills/SKILL_TEMPLATE.md +61 -29
  14. package/skills/code_review/SKILL.md +28 -0
  15. package/skills/code_review/examples.md +59 -0
  16. package/skills/code_review/risk.json +9 -0
  17. package/skills/code_review/schema.json +65 -0
  18. package/skills/data_analysis/SKILL.md +28 -0
  19. package/skills/data_analysis/examples.md +62 -0
  20. package/skills/data_analysis/risk.json +9 -0
  21. package/skills/data_analysis/schema.json +61 -0
  22. package/skills/file_edit/SKILL.md +33 -0
  23. package/skills/file_edit/examples.md +45 -0
  24. package/skills/file_edit/risk.json +9 -0
  25. package/skills/file_edit/schema.json +60 -0
  26. package/skills/summarize_document/SKILL.md +68 -0
  27. package/skills/summarize_document/examples.md +65 -0
  28. package/skills/summarize_document/risk.json +9 -0
  29. package/skills/summarize_document/schema.json +71 -0
  30. package/skills/web_search/SKILL.md +28 -0
  31. package/skills/web_search/examples.md +61 -0
  32. package/skills/web_search/risk.json +9 -0
  33. package/skills/web_search/schema.json +62 -0
  34. package/static/account.html +53 -51
  35. package/static/admin.html +50 -46
  36. package/static/chat.html +124 -96
  37. package/static/graph.html +1231 -337
  38. package/static/manifest.json +2 -2
  39. package/tests/integration/__pycache__/__init__.cpython-314.pyc +0 -0
  40. package/tests/integration/__pycache__/test_api.cpython-314-pytest-9.0.3.pyc +0 -0
  41. package/tests/unit/__pycache__/test_tools.cpython-314-pytest-9.0.3.pyc +0 -0
  42. package/tests/unit/test_tools.py +194 -1
  43. package/tools.py +264 -4
@@ -2,56 +2,88 @@
2
2
 
3
3
  ## 메타데이터
4
4
  - **버전**: 0.1.0
5
- - **카테고리**: [coding | data | document | web | system | analysis]
6
- - **위험도**: [low | medium | high] <!-- low=읽기, medium=쓰기, high=실행/삭제 -->
7
- - **필요 권한**: [none | local_read | local_write | exec | admin]
5
+ - **카테고리**: coding | data | document | web | system | analysis
6
+ - **위험도**: low | medium | high
7
+ - **필요 권한**: none | local_read | local_write | exec | network | admin
8
8
 
9
9
  ## 설명
10
+
10
11
  한 줄 설명.
11
12
 
13
+ ## 거버넌스
14
+
15
+ `risk.json` 참고. 요약: `risk=read|write|exec|destructive`, `destructive=false`, `shell=false`, `network=false`, `auto_approve=true`, `sandbox=workspace|home|system`, `rollback=none|backup|git`
16
+
17
+ ## 트리거 조건
18
+
19
+ 호출해야 하는 상황:
20
+ - (예) 사용자가 "이 파일 고쳐줘", "수정해줘"라고 말할 때
21
+ - (예) 에이전트가 Implement 단계에서 코드 변경이 필요할 때
22
+
23
+ 호출하면 **안** 되는 상황:
24
+ - (예) 파일 내용 확인만 필요할 때 → `read_file` 사용
25
+ - (예) 디렉토리 목록 조회 → `list_dir` 사용
26
+
27
+ ## Side Effects
28
+
29
+ | 항목 | 내용 |
30
+ |------|------|
31
+ | 파일 변경 | 없음 |
32
+ | 생성 파일 | 없음 |
33
+ | 프로세스 | 없음 |
34
+ | 네트워크 | 없음 |
35
+
36
+ ## Rollback
37
+
38
+ | 항목 | 내용 |
39
+ |------|------|
40
+ | 가능 여부 | none / git |
41
+ | 방법 | (없음 / `git checkout <file>`) |
42
+ | 주의사항 | git 미초기화 시 rollback 불가 |
43
+
12
44
  ## 입력 스키마
13
- ```json
14
- {
15
- "required": ["field1"],
16
- "optional": ["field2"],
17
- "properties": {
18
- "field1": { "type": "string", "description": "..." },
19
- "field2": { "type": "integer", "default": 10 }
20
- }
21
- }
22
- ```
45
+
46
+ `schema.json` → `input` 블록 참고.
47
+
48
+ | 필드 | 타입 | 필수 | 설명 |
49
+ |------|------|------|------|
50
+ | `field1` | string | ✅ | ... |
51
+ | `field2` | integer | ❌ | 기본값: 10 |
23
52
 
24
53
  ## 출력 스키마
54
+
55
+ `schema.json` → `output` 블록 참고.
56
+
57
+ 성공:
58
+ ```json
59
+ { "success": true, "result": { ... } }
60
+ ```
61
+ 실패:
25
62
  ```json
26
- {
27
- "success": true,
28
- "result": "...",
29
- "artifacts": []
30
- }
63
+ { "success": false, "error": "ERROR_CODE", "message": "..." }
31
64
  ```
32
65
 
33
66
  ## 실행 조건
34
- - 사전 조건 (예: 모델 로드 필요, 파일 존재 여부)
35
- - 후처리 조건
36
67
 
37
- ## 예제
68
+ - 사전 조건 (예: 모델 로드 필요, 파일 존재 여부)
38
69
 
39
- ### 성공 케이스
40
- **입력**: `{ "field1": "예시값" }`
41
- **출력**: `{ "success": true, "result": "..." }`
70
+ ## 실패 처리
42
71
 
43
- ### 실패 케이스
44
- **입력**: `{ "field1": "" }`
45
- **출력**: `{ "success": false, "error": "field1 is required" }`
72
+ `schema.json` `evals` 블록 참고.
46
73
 
47
- ## 실패 처리
48
74
  | 에러 코드 | 원인 | 처리 방법 |
49
75
  |-----------|------|-----------|
50
76
  | `INVALID_INPUT` | 필수 필드 누락 | 입력 검증 후 재시도 |
51
77
  | `PERMISSION_DENIED` | 권한 없음 | 관리자에게 문의 |
52
78
  | `TIMEOUT` | 실행 시간 초과 | 작업 분할 후 재시도 |
53
79
 
80
+ ## 예제
81
+
82
+ `examples.md` 참고.
83
+
54
84
  ## 테스트 케이스
85
+
55
86
  ```python
56
- # tests/skills/test_<name>.py 참조
87
+ # tests/unit/test_tools.py::test_<name>_*
88
+ # tests/integration/test_api.py::test_agent_<name>_*
57
89
  ```
@@ -9,6 +9,34 @@
9
9
  ## 설명
10
10
  파일 또는 코드 스니펫을 LLM에 전달해 버그, 보안 이슈, 성능, 스타일을 리뷰한다.
11
11
 
12
+ ## 거버넌스
13
+
14
+ `policies/policy.md` 참고. 요약: `risk=read`, `destructive=false`, `shell=false`, `network=false(local)/true(cloud)`, `auto_approve=true`, `sandbox=workspace`, `rollback=none`
15
+
16
+ ## 트리거 조건
17
+
18
+ 호출해야 하는 상황:
19
+ - 사용자가 "이 코드 리뷰해줘", "보안 검토해줘", "버그 있어?"라고 요청할 때
20
+ - 에이전트가 Verify 단계에서 작성한 코드를 검증해야 할 때
21
+ - PR 제출 전 코드 품질 확인이 필요할 때
22
+
23
+ 호출하면 **안** 되는 상황:
24
+ - 코드를 수정해야 할 때 → `edit_file` 사용
25
+ - 파일 내용만 확인할 때 → `read_file` 사용
26
+
27
+ ## Side Effects
28
+
29
+ | 항목 | 내용 |
30
+ |------|------|
31
+ | 파일 변경 | 없음 |
32
+ | 생성 파일 | 없음 |
33
+ | 프로세스 | 없음 |
34
+ | 네트워크 | 클라우드 LLM 사용 시 코드 내용이 외부 API로 전송됨 |
35
+
36
+ ## Rollback
37
+
38
+ 없음. 읽기 전용 작업.
39
+
12
40
  ## 입력 스키마
13
41
  ```json
14
42
  {
@@ -0,0 +1,59 @@
1
+ # code_review — Examples
2
+
3
+ ## 1. Security review of a snippet (success)
4
+
5
+ **Input**
6
+ ```json
7
+ { "target": "def foo(x):\n return eval(x)\n", "focus": ["security"] }
8
+ ```
9
+ **Output**
10
+ ```json
11
+ {
12
+ "success": true,
13
+ "result": {
14
+ "summary": "1개의 심각한 보안 이슈 발견",
15
+ "issues": [{ "severity": "high", "line": 2, "category": "security", "message": "eval()에 사용자 입력이 직접 전달됨 — 임의 코드 실행 가능" }],
16
+ "score": 40
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## 2. File review (success)
22
+
23
+ **Input**
24
+ ```json
25
+ { "target": "~/project/server.py", "focus": ["bug","performance"], "max_lines": 200 }
26
+ ```
27
+ **Output**
28
+ ```json
29
+ {
30
+ "success": true,
31
+ "result": {
32
+ "summary": "전반적으로 양호. 1개의 중간 수준 버그 발견.",
33
+ "issues": [{ "severity": "medium", "line": 87, "category": "bug", "message": "race condition: shared dict에 lock 없이 동시 접근 가능" }],
34
+ "score": 75
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## 3. Empty target (failure)
40
+
41
+ **Input**
42
+ ```json
43
+ { "target": "" }
44
+ ```
45
+ **Output**
46
+ ```json
47
+ { "success": false, "error": "INVALID_INPUT", "message": "target is required" }
48
+ ```
49
+
50
+ ## 4. Model not loaded (failure)
51
+
52
+ **Input**
53
+ ```json
54
+ { "target": "x = 1\n" }
55
+ ```
56
+ **Output**
57
+ ```json
58
+ { "success": false, "error": "MODEL_NOT_LOADED", "message": "LLM이 로드되지 않았습니다. /model 명령으로 모델을 선택하세요." }
59
+ ```
@@ -0,0 +1,9 @@
1
+ {
2
+ "risk": "read",
3
+ "destructive": false,
4
+ "shell": false,
5
+ "network": false,
6
+ "auto_approve": true,
7
+ "sandbox": "workspace",
8
+ "rollback": "none"
9
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "code_review",
4
+ "input": {
5
+ "required": ["target"],
6
+ "optional": ["focus", "lang", "max_lines"],
7
+ "properties": {
8
+ "target": { "type": "string", "description": "절대 파일 경로 또는 코드 스니펫 문자열" },
9
+ "focus": { "type": "array", "items": { "type": "string", "enum": ["bug","security","performance","style"] }, "default": ["bug","security"] },
10
+ "lang": { "type": "string", "description": "언어 힌트 (python, js, go …). 미입력시 자동 감지" },
11
+ "max_lines": { "type": "integer", "default": 500 }
12
+ }
13
+ },
14
+ "output": {
15
+ "oneOf": [
16
+ {
17
+ "title": "success",
18
+ "properties": {
19
+ "success": { "const": true },
20
+ "result": {
21
+ "properties": {
22
+ "summary": { "type": "string" },
23
+ "issues": {
24
+ "type": "array",
25
+ "items": {
26
+ "properties": {
27
+ "severity": { "type": "string", "enum": ["high","medium","low"] },
28
+ "line": { "type": "integer" },
29
+ "category": { "type": "string" },
30
+ "message": { "type": "string" }
31
+ },
32
+ "required": ["severity","category","message"]
33
+ }
34
+ },
35
+ "score": { "type": "integer", "minimum": 0, "maximum": 100 }
36
+ },
37
+ "required": ["summary","issues","score"]
38
+ }
39
+ },
40
+ "required": ["success","result"]
41
+ },
42
+ {
43
+ "title": "failure",
44
+ "properties": {
45
+ "success": { "const": false },
46
+ "error": { "type": "string", "enum": ["INVALID_INPUT","FILE_NOT_FOUND","MODEL_NOT_LOADED","SIZE_LIMIT"] },
47
+ "message": { "type": "string" }
48
+ },
49
+ "required": ["success","error","message"]
50
+ }
51
+ ]
52
+ },
53
+ "evals": [
54
+ {
55
+ "id": "snippet_review",
56
+ "input": { "target": "def foo(x):\n return eval(x)\n", "focus": ["security"] },
57
+ "pass_criteria": "success == true and score < 80"
58
+ },
59
+ {
60
+ "id": "empty_target",
61
+ "input": { "target": "" },
62
+ "pass_criteria": "error == INVALID_INPUT"
63
+ }
64
+ ]
65
+ }
@@ -9,6 +9,34 @@
9
9
  ## 설명
10
10
  CSV/Excel/JSON 파일을 읽어 기초 통계, 컬럼 요약, 이상치 탐지를 수행하고 인사이트를 제공한다.
11
11
 
12
+ ## 거버넌스
13
+
14
+ `policies/policy.md` 참고. 요약: `risk=read`, `destructive=false`, `shell=false`, `network=false`, `auto_approve=true`, `sandbox=home`, `rollback=none`
15
+
16
+ ## 트리거 조건
17
+
18
+ 호출해야 하는 상황:
19
+ - 사용자가 "이 CSV 분석해줘", "이 데이터 통계 내줘", "이상치 찾아줘"라고 요청할 때
20
+ - 에이전트가 Discover 단계에서 데이터 파일의 구조를 파악해야 할 때
21
+ - 상관관계, 추세, 분포를 파악해야 할 때
22
+
23
+ 호출하면 **안** 되는 상황:
24
+ - 파일을 수정해야 할 때 → `edit_file` / `write_file` 사용
25
+ - .csv가 아닌 텍스트 파일 내용 검색 → `grep` 사용
26
+
27
+ ## Side Effects
28
+
29
+ | 항목 | 내용 |
30
+ |------|------|
31
+ | 파일 변경 | 없음 |
32
+ | 생성 파일 | 없음 |
33
+ | 프로세스 | 없음 |
34
+ | 네트워크 | 없음 |
35
+
36
+ ## Rollback
37
+
38
+ 없음. 읽기 전용 작업.
39
+
12
40
  ## 입력 스키마
13
41
  ```json
14
42
  {
@@ -0,0 +1,62 @@
1
+ # data_analysis — Examples
2
+
3
+ ## 1. CSV summary + outlier detection (success)
4
+
5
+ **Input**
6
+ ```json
7
+ { "path": "~/data/sales.csv", "analysis_type": ["summary", "outlier"] }
8
+ ```
9
+ **Output**
10
+ ```json
11
+ {
12
+ "success": true,
13
+ "result": {
14
+ "shape": [500, 4],
15
+ "columns": ["date", "revenue", "units", "region"],
16
+ "summary": { "revenue": { "mean": 15000.0, "std": 4200.0, "min": 200, "max": 89000 } },
17
+ "outliers": { "revenue": [89000] },
18
+ "insights": "revenue 컬럼에서 1개의 이상치(89000) 발견. 평균 대비 17.6 표준편차."
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## 2. Correlation analysis (success)
24
+
25
+ **Input**
26
+ ```json
27
+ { "path": "~/data/metrics.csv", "columns": ["cpu", "latency"], "analysis_type": ["correlation"] }
28
+ ```
29
+ **Output**
30
+ ```json
31
+ {
32
+ "success": true,
33
+ "result": {
34
+ "shape": [1000, 2],
35
+ "columns": ["cpu", "latency"],
36
+ "summary": { "cpu": { "mean": 45.2 }, "latency": { "mean": 120.5 } },
37
+ "insights": "cpu와 latency 간 강한 양의 상관관계 (r=0.87) 확인."
38
+ }
39
+ }
40
+ ```
41
+
42
+ ## 3. Unsupported format (failure)
43
+
44
+ **Input**
45
+ ```json
46
+ { "path": "~/data/photo.png" }
47
+ ```
48
+ **Output**
49
+ ```json
50
+ { "success": false, "error": "UNSUPPORTED_FORMAT", "message": "Supported formats: csv, xlsx, json" }
51
+ ```
52
+
53
+ ## 4. File not found (failure)
54
+
55
+ **Input**
56
+ ```json
57
+ { "path": "~/data/missing.csv" }
58
+ ```
59
+ **Output**
60
+ ```json
61
+ { "success": false, "error": "FILE_NOT_FOUND", "message": "No such file: /home/user/data/missing.csv" }
62
+ ```
@@ -0,0 +1,9 @@
1
+ {
2
+ "risk": "read",
3
+ "destructive": false,
4
+ "shell": false,
5
+ "network": false,
6
+ "auto_approve": true,
7
+ "sandbox": "home",
8
+ "rollback": "none"
9
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "data_analysis",
4
+ "input": {
5
+ "required": ["path"],
6
+ "optional": ["columns", "analysis_type", "max_rows"],
7
+ "properties": {
8
+ "path": { "type": "string", "description": "분석할 파일 절대 경로 (.csv, .xlsx, .json)" },
9
+ "columns": { "type": "array", "items": { "type": "string" }, "description": "분석할 컬럼 목록. 미입력시 전체" },
10
+ "analysis_type": { "type": "array", "items": { "type": "string", "enum": ["summary","outlier","correlation","trend"] }, "default": ["summary"] },
11
+ "max_rows": { "type": "integer", "default": 10000 }
12
+ }
13
+ },
14
+ "output": {
15
+ "oneOf": [
16
+ {
17
+ "title": "success",
18
+ "properties": {
19
+ "success": { "const": true },
20
+ "result": {
21
+ "properties": {
22
+ "shape": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2 },
23
+ "columns": { "type": "array", "items": { "type": "string" } },
24
+ "summary": { "type": "object" },
25
+ "outliers": { "type": "object" },
26
+ "insights": { "type": "string" }
27
+ },
28
+ "required": ["shape","columns","summary"]
29
+ }
30
+ },
31
+ "required": ["success","result"]
32
+ },
33
+ {
34
+ "title": "failure",
35
+ "properties": {
36
+ "success": { "const": false },
37
+ "error": { "type": "string", "enum": ["INVALID_INPUT","FILE_NOT_FOUND","UNSUPPORTED_FORMAT","PARSE_ERROR","SIZE_LIMIT"] },
38
+ "message": { "type": "string" }
39
+ },
40
+ "required": ["success","error","message"]
41
+ }
42
+ ]
43
+ },
44
+ "evals": [
45
+ {
46
+ "id": "csv_summary",
47
+ "input": { "path": "__TEST__/sales.csv", "analysis_type": ["summary"] },
48
+ "pass_criteria": "success == true and shape[0] > 0"
49
+ },
50
+ {
51
+ "id": "unsupported_format",
52
+ "input": { "path": "__TEST__/photo.png" },
53
+ "pass_criteria": "error == UNSUPPORTED_FORMAT"
54
+ },
55
+ {
56
+ "id": "missing_path",
57
+ "input": {},
58
+ "pass_criteria": "error == INVALID_INPUT"
59
+ }
60
+ ]
61
+ }
@@ -9,6 +9,39 @@
9
9
  ## 설명
10
10
  로컬 파일을 읽고 특정 범위를 편집한 뒤 저장한다. 원본 백업을 `.bak` 파일로 생성한다.
11
11
 
12
+ ## 거버넌스
13
+
14
+ `policies/policy.md` 참고. 요약: `risk=write`, `destructive=false`, `shell=false`, `network=false`, `auto_approve=false`, `sandbox=workspace`, `rollback=git`
15
+
16
+ ## 트리거 조건
17
+
18
+ 호출해야 하는 상황:
19
+ - 에이전트가 Implement 단계에서 코드/설정 파일을 변경해야 할 때
20
+ - 사용자가 "이 부분 고쳐줘", "이 라인 바꿔줘", "수정해줘"라고 요청할 때
21
+ - `read_file`로 내용 확인 후 특정 문자열을 교체해야 할 때
22
+
23
+ 호출하면 **안** 되는 상황:
24
+ - 파일 내용 확인만 필요할 때 → `read_file` 사용
25
+ - 파일 전체를 처음부터 새로 쓸 때 → `write_file` 사용
26
+ - 바이너리 파일(이미지, ZIP 등) 수정 시도 → 불가, `BINARY_FILE` 에러
27
+
28
+ ## Side Effects
29
+
30
+ | 항목 | 내용 |
31
+ |------|------|
32
+ | 파일 변경 | 대상 파일의 내용이 영구 변경됨 |
33
+ | 생성 파일 | 없음 (`backup=false` 기본) |
34
+ | 프로세스 | 없음 |
35
+ | 네트워크 | 없음 |
36
+
37
+ ## Rollback
38
+
39
+ | 항목 | 내용 |
40
+ |------|------|
41
+ | 가능 여부 | `git` — git이 초기화된 워크스페이스에서 복구 가능 |
42
+ | 방법 | `git diff <file>` 확인 후 `git checkout <file>` |
43
+ | 주의사항 | git 미초기화 시 rollback 불가 (`.bak` 파일 없음, backup=false 기본) |
44
+
12
45
  ## 입력 스키마
13
46
  ```json
14
47
  {
@@ -0,0 +1,45 @@
1
+ # file_edit — Examples
2
+
3
+ ## 1. Single-line replacement (success)
4
+
5
+ **Input**
6
+ ```json
7
+ { "path": "~/project/config.py", "new_content": "DEBUG = False\n", "start_line": 5, "end_line": 5 }
8
+ ```
9
+ **Output**
10
+ ```json
11
+ { "success": true, "result": { "path": "/home/user/project/config.py", "lines_changed": 1, "backup_path": null } }
12
+ ```
13
+
14
+ ## 2. Full file replacement (success)
15
+
16
+ **Input**
17
+ ```json
18
+ { "path": "~/project/hello.py", "new_content": "print('hello')\n" }
19
+ ```
20
+ **Output**
21
+ ```json
22
+ { "success": true, "result": { "path": "/home/user/project/hello.py", "lines_changed": 1, "backup_path": null } }
23
+ ```
24
+
25
+ ## 3. Binary file rejected (failure)
26
+
27
+ **Input**
28
+ ```json
29
+ { "path": "~/photo.png", "new_content": "..." }
30
+ ```
31
+ **Output**
32
+ ```json
33
+ { "success": false, "error": "BINARY_FILE", "message": "Binary files cannot be edited as text" }
34
+ ```
35
+
36
+ ## 4. File not found (failure)
37
+
38
+ **Input**
39
+ ```json
40
+ { "path": "~/nonexistent.py", "new_content": "x = 1\n", "start_line": 1 }
41
+ ```
42
+ **Output**
43
+ ```json
44
+ { "success": false, "error": "FILE_NOT_FOUND", "message": "No such file: /home/user/nonexistent.py" }
45
+ ```
@@ -0,0 +1,9 @@
1
+ {
2
+ "risk": "write",
3
+ "destructive": false,
4
+ "shell": false,
5
+ "network": false,
6
+ "auto_approve": false,
7
+ "sandbox": "workspace",
8
+ "rollback": "git"
9
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "file_edit",
4
+ "input": {
5
+ "required": ["path", "new_content"],
6
+ "optional": ["start_line", "end_line", "backup"],
7
+ "properties": {
8
+ "path": { "type": "string", "description": "절대 경로 또는 ~/로 시작하는 경로" },
9
+ "new_content": { "type": "string", "description": "교체할 새 내용" },
10
+ "start_line": { "type": "integer", "description": "편집 시작 줄 번호 (1-indexed). 없으면 전체 교체" },
11
+ "end_line": { "type": "integer", "description": "편집 종료 줄 번호 (포함). 없으면 start_line 단일 줄" },
12
+ "backup": { "type": "boolean", "default": false, "description": "true면 .bak 백업 생성" }
13
+ }
14
+ },
15
+ "output": {
16
+ "oneOf": [
17
+ {
18
+ "title": "success",
19
+ "properties": {
20
+ "success": { "const": true },
21
+ "result": {
22
+ "properties": {
23
+ "path": { "type": "string" },
24
+ "lines_changed": { "type": "integer" },
25
+ "backup_path": { "type": ["string", "null"] }
26
+ },
27
+ "required": ["path", "lines_changed"]
28
+ }
29
+ },
30
+ "required": ["success", "result"]
31
+ },
32
+ {
33
+ "title": "failure",
34
+ "properties": {
35
+ "success": { "const": false },
36
+ "error": { "type": "string", "enum": ["INVALID_INPUT","FILE_NOT_FOUND","BINARY_FILE","PERMISSION_DENIED","SIZE_LIMIT"] },
37
+ "message": { "type": "string" }
38
+ },
39
+ "required": ["success", "error", "message"]
40
+ }
41
+ ]
42
+ },
43
+ "evals": [
44
+ {
45
+ "id": "full_replace_success",
46
+ "input": { "path": "__TEST__/config.py", "new_content": "DEBUG = False\n" },
47
+ "pass_criteria": "success == true and lines_changed >= 1"
48
+ },
49
+ {
50
+ "id": "binary_rejected",
51
+ "input": { "path": "__TEST__/photo.png", "new_content": "..." },
52
+ "pass_criteria": "error == BINARY_FILE"
53
+ },
54
+ {
55
+ "id": "missing_path",
56
+ "input": { "new_content": "x = 1" },
57
+ "pass_criteria": "error == INVALID_INPUT"
58
+ }
59
+ ]
60
+ }