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.
- package/README.md +174 -305
- package/docs/CHANGELOG.md +307 -0
- package/docs/architecture.md +121 -0
- package/docs/mcp-tools.md +116 -0
- package/docs/privacy.md +74 -0
- package/docs/public-deploy.md +137 -0
- package/docs/security-model.md +121 -0
- package/knowledge_graph.py +123 -15
- package/llm_router.py +100 -28
- package/ltcai_cli.py +138 -5
- package/package.json +14 -2
- package/server.py +1756 -329
- package/skills/SKILL_TEMPLATE.md +61 -29
- package/skills/code_review/SKILL.md +28 -0
- package/skills/code_review/examples.md +59 -0
- package/skills/code_review/risk.json +9 -0
- package/skills/code_review/schema.json +65 -0
- package/skills/data_analysis/SKILL.md +28 -0
- package/skills/data_analysis/examples.md +62 -0
- package/skills/data_analysis/risk.json +9 -0
- package/skills/data_analysis/schema.json +61 -0
- package/skills/file_edit/SKILL.md +33 -0
- package/skills/file_edit/examples.md +45 -0
- package/skills/file_edit/risk.json +9 -0
- package/skills/file_edit/schema.json +60 -0
- package/skills/summarize_document/SKILL.md +68 -0
- package/skills/summarize_document/examples.md +65 -0
- package/skills/summarize_document/risk.json +9 -0
- package/skills/summarize_document/schema.json +71 -0
- package/skills/web_search/SKILL.md +28 -0
- package/skills/web_search/examples.md +61 -0
- package/skills/web_search/risk.json +9 -0
- package/skills/web_search/schema.json +62 -0
- package/static/account.html +53 -51
- package/static/admin.html +50 -46
- package/static/chat.html +124 -96
- package/static/graph.html +1231 -337
- package/static/manifest.json +2 -2
- package/tests/integration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/integration/__pycache__/test_api.cpython-314-pytest-9.0.3.pyc +0 -0
- package/tests/unit/__pycache__/test_tools.cpython-314-pytest-9.0.3.pyc +0 -0
- package/tests/unit/test_tools.py +194 -1
- package/tools.py +264 -4
package/skills/SKILL_TEMPLATE.md
CHANGED
|
@@ -2,56 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
## 메타데이터
|
|
4
4
|
- **버전**: 0.1.0
|
|
5
|
-
- **카테고리**:
|
|
6
|
-
- **위험도**:
|
|
7
|
-
- **필요 권한**:
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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/
|
|
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,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,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,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
|
+
}
|