opencode-hashline 1.0.0
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/LICENSE +21 -0
- package/README.md +442 -0
- package/README.ru.md +417 -0
- package/dist/chunk-C2EVIAGV.js +177 -0
- package/dist/chunk-IVZSANZ4.js +411 -0
- package/dist/hashline-Civwirvf.d.cts +278 -0
- package/dist/hashline-Civwirvf.d.ts +278 -0
- package/dist/hashline-W2FT5QN4.js +44 -0
- package/dist/index.cjs +811 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +197 -0
- package/dist/utils.cjs +637 -0
- package/dist/utils.d.cts +74 -0
- package/dist/utils.d.ts +74 -0
- package/dist/utils.js +54 -0
- package/package.json +56 -0
package/README.ru.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# π opencode-hashline
|
|
4
|
+
|
|
5
|
+
**ΠΠΎΠ½ΡΠ΅Π½ΡΠ½ΠΎ-Π°Π΄ΡΠ΅ΡΡΠ΅ΠΌΠΎΠ΅ Ρ
Π΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΡΡΡΠΎΠΊ Π΄Π»Ρ ΡΠΎΡΠ½ΠΎΠ³ΠΎ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΊΠΎΠ΄Π° Ρ ΠΏΠΎΠΌΠΎΡΡΡ AI**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/izzzzzi/opencode-hashline/actions/workflows/ci.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/opencode-hashline)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://nodejs.org/)
|
|
12
|
+
|
|
13
|
+
**π·πΊ Π ΡΡΡΠΊΠΈΠΉ** | [π¬π§ English](README.md)
|
|
14
|
+
|
|
15
|
+
<br />
|
|
16
|
+
|
|
17
|
+
*Hashline-ΠΏΠ»Π°Π³ΠΈΠ½ Π΄Π»Ρ [OpenCode](https://github.com/anomalyco/opencode) β Π°Π½Π½ΠΎΡΠΈΡΡΠ΅Ρ ΠΊΠ°ΠΆΠ΄ΡΡ ΡΡΡΠΎΠΊΡ ΡΠ°ΠΉΠ»Π° Π΄Π΅ΡΠ΅ΡΠΌΠΈΠ½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ Ρ
Π΅Ρ-ΡΠ΅Π³ΠΎΠΌ, ΡΡΠΎΠ±Ρ AI ΠΌΠΎΠ³ ΡΡΡΠ»Π°ΡΡΡΡ Π½Π° ΠΊΠΎΠ΄ ΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ Ρ Ρ
ΠΈΡΡΡΠ³ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΎΡΠ½ΠΎΡΡΡΡ.*
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## π Π§ΡΠΎ ΡΠ°ΠΊΠΎΠ΅ Hashline?
|
|
24
|
+
|
|
25
|
+
Hashline Π°Π½Π½ΠΎΡΠΈΡΡΠ΅Ρ ΠΊΠ°ΠΆΠ΄ΡΡ ΡΡΡΠΎΠΊΡ ΡΠ°ΠΉΠ»Π° ΠΊΠΎΡΠΎΡΠΊΠΈΠΌ Π΄Π΅ΡΠ΅ΡΠΌΠΈΠ½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ hex-Ρ
Π΅ΡΠ΅ΠΌ. ΠΠΎΠ³Π΄Π° AI ΡΠΈΡΠ°Π΅Ρ ΡΠ°ΠΉΠ», ΠΎΠ½ Π²ΠΈΠ΄ΠΈΡ:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
#HL 1:a3f|function hello() {
|
|
29
|
+
#HL 2:f1c| return "world";
|
|
30
|
+
#HL 3:0e7|}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> **ΠΡΠΈΠΌΠ΅ΡΠ°Π½ΠΈΠ΅:** ΠΠ»ΠΈΠ½Π° Ρ
Π΅ΡΠ° Π°Π΄Π°ΠΏΡΠΈΠ²Π½Π°Ρ β ΠΎΠ½Π° Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΡΠ°ΠΉΠ»Π° (2 ΡΠΈΠΌΠ²ΠΎΠ»Π° Π΄Π»Ρ β€256 ΡΡΡΠΎΠΊ, 3 ΡΠΈΠΌΠ²ΠΎΠ»Π° Π΄Π»Ρ β€4096 ΡΡΡΠΎΠΊ, 4 ΡΠΈΠΌΠ²ΠΎΠ»Π° Π΄Π»Ρ >4096 ΡΡΡΠΎΠΊ). Π ΠΏΡΠΈΠΌΠ΅ΡΠ°Ρ
Π½ΠΈΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡΡΡ 3-ΡΠΈΠΌΠ²ΠΎΠ»ΡΠ½ΡΠ΅ Ρ
Π΅ΡΠΈ. ΠΡΠ΅ΡΠΈΠΊΡ `#HL ` Π·Π°ΡΠΈΡΠ°Π΅Ρ ΠΎΡ Π»ΠΎΠΆΠ½ΡΡ
ΡΡΠ°Π±Π°ΡΡΠ²Π°Π½ΠΈΠΉ ΠΏΡΠΈ ΡΠ΄Π°Π»Π΅Π½ΠΈΠΈ Ρ
Π΅ΡΠ΅ΠΉ ΠΈ ΡΠ²Π»ΡΠ΅ΡΡΡ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌΡΠΌ.
|
|
34
|
+
|
|
35
|
+
AI-ΠΌΠΎΠ΄Π΅Π»Ρ ΠΌΠΎΠΆΠ΅Ρ ΡΡΡΠ»Π°ΡΡΡΡ Π½Π° ΡΡΡΠΎΠΊΠΈ ΠΏΠΎ ΠΈΡ
Ρ
Π΅Ρ-ΡΠ΅Π³Π°ΠΌ Π΄Π»Ρ ΡΠΎΡΠ½ΠΎΠ³ΠΎ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ:
|
|
36
|
+
|
|
37
|
+
- **Β«ΠΠ°ΠΌΠ΅Π½ΠΈΡΡ ΡΡΡΠΎΠΊΡ `2:f1c`Β»** β ΡΠΊΠ°Π·Π°ΡΡ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΡ ΡΡΡΠΎΠΊΡ ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΠΎ
|
|
38
|
+
- **Β«ΠΠ°ΠΌΠ΅Π½ΠΈΡΡ Π±Π»ΠΎΠΊ ΠΎΡ `1:a3f` Π΄ΠΎ `3:0e7`Β»** β ΡΠΊΠ°Π·Π°ΡΡ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ ΡΡΡΠΎΠΊ
|
|
39
|
+
- **Β«ΠΡΡΠ°Π²ΠΈΡΡ ΠΏΠΎΡΠ»Π΅ `3:0e7`Β»** β Π²ΡΡΠ°Π²ΠΈΡΡ Π² ΡΠΎΡΠ½ΠΎΠ΅ ΠΌΠ΅ΡΡΠΎ
|
|
40
|
+
|
|
41
|
+
### π€ ΠΠΎΡΠ΅ΠΌΡ ΡΡΠΎ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ?
|
|
42
|
+
|
|
43
|
+
Π’ΡΠ°Π΄ΠΈΡΠΈΠΎΠ½Π½ΡΠ΅ Π½ΠΎΠΌΠ΅ΡΠ° ΡΡΡΠΎΠΊ ΡΠ΄Π²ΠΈΠ³Π°ΡΡΡΡ ΠΏΡΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ, Π²ΡΠ·ΡΠ²Π°Ρ ΠΎΡΠΈΠ±ΠΊΠΈ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΈ ΡΡΡΠ°ΡΠ΅Π²ΡΠΈΠ΅ ΡΡΡΠ»ΠΊΠΈ. Π₯Π΅Ρ-ΡΠ΅Π³ΠΈ Hashline **ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ½ΠΎ-Π°Π΄ΡΠ΅ΡΡΠ΅ΠΌΡ** β ΠΎΠ½ΠΈ Π²ΡΡΠΈΡΠ»ΡΡΡΡΡ ΠΈΠ· ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΡΡΡΠΎΠΊΠΈ ΠΈ Π΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ, ΡΡΠΎ Π΄Π΅Π»Π°Π΅Ρ ΠΈΡ
ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎΠΉ, Π²Π΅ΡΠΈΡΠΈΡΠΈΡΡΠ΅ΠΌΠΎΠΉ ΡΡΡΠ»ΠΊΠΎΠΉ Π΄Π»Ρ ΡΠΎΡΠ½ΠΎΠΉ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈ ΠΎ ΠΌΠ΅ΡΡΠΎΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ Π² ΠΊΠΎΠ΄Π΅.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## β¨ ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
|
|
48
|
+
|
|
49
|
+
### π ΠΠ΄Π°ΠΏΡΠΈΠ²Π½Π°Ρ Π΄Π»ΠΈΠ½Π° Ρ
Π΅ΡΠ°
|
|
50
|
+
|
|
51
|
+
ΠΠ»ΠΈΠ½Π° Ρ
Π΅ΡΠ° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π°Π΄Π°ΠΏΡΠΈΡΡΠ΅ΡΡΡ ΠΊ ΡΠ°Π·ΠΌΠ΅ΡΡ ΡΠ°ΠΉΠ»Π° Π΄Π»Ρ ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ ΠΊΠΎΠ»Π»ΠΈΠ·ΠΈΠΉ:
|
|
52
|
+
|
|
53
|
+
| Π Π°Π·ΠΌΠ΅Ρ ΡΠ°ΠΉΠ»Π° | ΠΠ»ΠΈΠ½Π° Ρ
Π΅ΡΠ° | ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΡΡ
Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ |
|
|
54
|
+
|-------------|:----------:|:------------------:|
|
|
55
|
+
| β€ 256 ΡΡΡΠΎΠΊ | 2 hex-ΡΠΈΠΌΠ²ΠΎΠ»Π° | 256 |
|
|
56
|
+
| β€ 4 096 ΡΡΡΠΎΠΊ | 3 hex-ΡΠΈΠΌΠ²ΠΎΠ»Π° | 4 096 |
|
|
57
|
+
| > 4 096 ΡΡΡΠΎΠΊ | 4 hex-ΡΠΈΠΌΠ²ΠΎΠ»Π° | 65 536 |
|
|
58
|
+
|
|
59
|
+
### π·οΈ ΠΠ°Π³ΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΡΠ΅ΡΠΈΠΊΡ (`#HL `)
|
|
60
|
+
|
|
61
|
+
Π‘ΡΡΠΎΠΊΠΈ Π°Π½Π½ΠΎΡΠΈΡΡΡΡΡΡ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌΡΠΌ ΠΏΡΠ΅ΡΠΈΠΊΡΠΎΠΌ (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ: `#HL `), ΡΡΠΎΠ±Ρ ΠΏΡΠ΅Π΄ΠΎΡΠ²ΡΠ°ΡΠΈΡΡ Π»ΠΎΠΆΠ½ΡΠ΅ ΡΡΠ°Π±Π°ΡΡΠ²Π°Π½ΠΈΡ ΠΏΡΠΈ ΡΠ΄Π°Π»Π΅Π½ΠΈΠΈ Ρ
Π΅ΡΠ΅ΠΉ. ΠΡΠΎ Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ, ΡΡΠΎ ΡΡΡΠΎΠΊΠΈ Π΄Π°Π½Π½ΡΡ
Π²ΡΠΎΠ΄Π΅ `1:ab|some data` Π½Π΅ Π±ΡΠ΄ΡΡ ΡΠ»ΡΡΠ°ΠΉΠ½ΠΎ ΠΎΠ±ΡΠ΅Π·Π°Π½Ρ.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
#HL 1:a3|function hello() {
|
|
65
|
+
#HL 2:f1| return "world";
|
|
66
|
+
#HL 3:0e|}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
ΠΡΠ΅ΡΠΈΠΊΡ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡΡΠΎΠΈΡΡ ΠΈΠ»ΠΈ ΠΎΡΠΊΠ»ΡΡΠΈΡΡ Π΄Π»Ρ ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠΉ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// ΠΠ°ΡΡΠΎΠΌΠ½ΡΠΉ ΠΏΡΠ΅ΡΠΈΠΊΡ
|
|
73
|
+
const hl = createHashline({ prefix: ">> " });
|
|
74
|
+
|
|
75
|
+
// ΠΡΠΊΠ»ΡΡΠΈΡΡ ΠΏΡΠ΅ΡΠΈΠΊΡ (legacy-ΡΠΎΡΠΌΠ°Ρ: "1:a3|code")
|
|
76
|
+
const hl = createHashline({ prefix: false });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### πΎ LRU-ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
|
|
80
|
+
|
|
81
|
+
ΠΡΡΡΠΎΠ΅Π½Π½ΡΠΉ LRU-ΠΊΠ΅Ρ (`filePath β annotatedContent`) Ρ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌΡΠΌ ΡΠ°Π·ΠΌΠ΅ΡΠΎΠΌ (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ 100 ΡΠ°ΠΉΠ»ΠΎΠ²). ΠΡΠΈ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠΌ ΡΡΠ΅Π½ΠΈΠΈ ΡΠΎΠ³ΠΎ ΠΆΠ΅ ΡΠ°ΠΉΠ»Π° Ρ Π½Π΅ΠΈΠ·ΠΌΠ΅Π½ΡΠ½Π½ΡΠΌ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΡΠΌ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΡΡΡ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ. ΠΠ΅Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΈΠ½Π²Π°Π»ΠΈΠ΄ΠΈΡΡΠ΅ΡΡΡ ΠΏΡΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π°.
|
|
82
|
+
|
|
83
|
+
### β
ΠΠ΅ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ Ρ
Π΅ΡΠ΅ΠΉ
|
|
84
|
+
|
|
85
|
+
ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΎΠ³ΠΎ, ΡΡΠΎ ΡΡΡΠΎΠΊΠ° Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»Π°ΡΡ Ρ ΠΌΠΎΠΌΠ΅Π½ΡΠ° ΡΡΠ΅Π½ΠΈΡ β Π·Π°ΡΠΈΡΠ° ΠΎΡ race conditions:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { verifyHash } from "opencode-hashline";
|
|
89
|
+
|
|
90
|
+
const result = verifyHash(2, "f1c", currentContent);
|
|
91
|
+
if (!result.valid) {
|
|
92
|
+
console.error(result.message); // "Hash mismatch at line 2: ..."
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
ΠΠ΅ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ Ρ
Π΅ΡΠ΅ΠΉ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Π΄Π»ΠΈΠ½Ρ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»Π΅Π½Π½ΠΎΠΉ Ρ
Π΅Ρ-ΡΡΡΠ»ΠΊΠΈ (Π° Π½Π΅ ΡΠ΅ΠΊΡΡΠΈΠΉ ΡΠ°Π·ΠΌΠ΅Ρ ΡΠ°ΠΉΠ»Π°), ΠΏΠΎΡΡΠΎΠΌΡ ΡΡΡΠ»ΠΊΠ° Π²ΡΠΎΠ΄Π΅ `2:f1` ΠΎΡΡΠ°ΡΡΡΡ Π²Π°Π»ΠΈΠ΄Π½ΠΎΠΉ Π΄Π°ΠΆΠ΅ Π΅ΡΠ»ΠΈ ΡΠ°ΠΉΠ» Π²ΡΡΠΎΡ.
|
|
97
|
+
|
|
98
|
+
### π Π§ΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΠΊ ΠΎΡΡΡΡΠΏΠ°ΠΌ
|
|
99
|
+
|
|
100
|
+
ΠΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Ρ
Π΅ΡΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ `trimEnd()` (Π° Π½Π΅ `trim()`), ΠΏΠΎΡΡΠΎΠΌΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π²Π΅Π΄ΡΡΠΈΡ
ΠΏΡΠΎΠ±Π΅Π»ΠΎΠ² (ΠΎΡΡΡΡΠΏΠΎΠ²) ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ²Π°ΡΡΡΡ ΠΊΠ°ΠΊ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ, Π° Π·Π°Π²Π΅ΡΡΠ°ΡΡΠΈΠ΅ ΠΏΡΠΎΠ±Π΅Π»Ρ ΠΈΠ³Π½ΠΎΡΠΈΡΡΡΡΡΡ.
|
|
101
|
+
|
|
102
|
+
### π Range-ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ
|
|
103
|
+
|
|
104
|
+
Π Π΅Π·ΠΎΠ»Π²ΠΈΠ½Π³ ΠΈ Π·Π°ΠΌΠ΅Π½Π° Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ΠΎΠ² ΡΡΡΠΎΠΊ ΠΏΠΎ Ρ
Π΅Ρ-ΡΡΡΠ»ΠΊΠ°ΠΌ:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { resolveRange, replaceRange } from "opencode-hashline";
|
|
108
|
+
|
|
109
|
+
// ΠΠΎΠ»ΡΡΠΈΡΡ ΡΡΡΠΎΠΊΠΈ ΠΌΠ΅ΠΆΠ΄Ρ Π΄Π²ΡΠΌΡ Ρ
Π΅Ρ-ΡΡΡΠ»ΠΊΠ°ΠΌΠΈ
|
|
110
|
+
const range = resolveRange("1:a3f", "3:0e7", content);
|
|
111
|
+
console.log(range.lines); // ["function hello() {", ' return "world";', "}"]
|
|
112
|
+
|
|
113
|
+
// ΠΠ°ΠΌΠ΅Π½ΠΈΡΡ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ Π½ΠΎΠ²ΡΠΌ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΡΠΌ
|
|
114
|
+
const newContent = replaceRange(
|
|
115
|
+
"1:a3f", "3:0e7", content,
|
|
116
|
+
"function goodbye() {\n return 'farewell';\n}"
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### βοΈ ΠΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΡΠ΅ΠΌΠΎΡΡΡ
|
|
121
|
+
|
|
122
|
+
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠ°ΡΡΠΎΠΌΠ½ΡΡ
ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠΎΠ² Hashline Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΡΠΌΠΈ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°ΠΌΠΈ:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { createHashline } from "opencode-hashline";
|
|
126
|
+
|
|
127
|
+
const hl = createHashline({
|
|
128
|
+
exclude: ["**/node_modules/**", "**/*.min.js"],
|
|
129
|
+
maxFileSize: 512_000, // 512 ΠΠ
|
|
130
|
+
hashLength: 3, // ΠΏΡΠΈΠ½ΡΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎ 3-ΡΠΈΠΌΠ²ΠΎΠ»ΡΠ½ΡΠ΅ Ρ
Π΅ΡΠΈ
|
|
131
|
+
cacheSize: 200, // ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°ΡΡ Π΄ΠΎ 200 ΡΠ°ΠΉΠ»ΠΎΠ²
|
|
132
|
+
prefix: "#HL ", // ΠΌΠ°Π³ΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΡΠ΅ΡΠΈΠΊΡ (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ)
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π°ΡΡΡΠΎΠ΅Π½Π½ΠΎΠ³ΠΎ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ°
|
|
136
|
+
const annotated = hl.formatFileWithHashes(content, "src/app.ts");
|
|
137
|
+
const isExcluded = hl.shouldExclude("node_modules/foo.js"); // true
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
141
|
+
|
|
142
|
+
| ΠΠ°ΡΠ°ΠΌΠ΅ΡΡ | Π’ΠΈΠΏ | ΠΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ | ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ |
|
|
143
|
+
|----------|-----|:------------:|----------|
|
|
144
|
+
| `exclude` | `string[]` | Π‘ΠΌ. Π½ΠΈΠΆΠ΅ | Glob-ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ Π΄Π»Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² |
|
|
145
|
+
| `maxFileSize` | `number` | `1_000_000` | ΠΠ°ΠΊΡ. ΡΠ°Π·ΠΌΠ΅Ρ ΡΠ°ΠΉΠ»Π° Π² Π±Π°ΠΉΡΠ°Ρ
|
|
|
146
|
+
| `hashLength` | `number \| undefined` | `undefined` (Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΠΎ) | ΠΡΠΈΠ½ΡΠ΄ΠΈΡΠ΅Π»ΡΠ½Π°Ρ Π΄Π»ΠΈΠ½Π° Ρ
Π΅ΡΠ° |
|
|
147
|
+
| `cacheSize` | `number` | `100` | ΠΠ°ΠΊΡ. ΡΠ°ΠΉΠ»ΠΎΠ² Π² LRU-ΠΊΠ΅ΡΠ΅ |
|
|
148
|
+
| `prefix` | `string \| false` | `"#HL "` | ΠΡΠ΅ΡΠΈΠΊΡ ΡΡΡΠΎΠΊΠΈ (`false` Π΄Π»Ρ ΠΎΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ) |
|
|
149
|
+
|
|
150
|
+
ΠΠ°ΡΡΠ΅ΡΠ½Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ: lock-ΡΠ°ΠΉΠ»Ρ, `node_modules`, ΠΌΠΈΠ½ΠΈΡΠΈΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ, Π±ΠΈΠ½Π°ΡΠ½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ (ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ, ΡΡΠΈΡΡΡ, Π°ΡΡ
ΠΈΠ²Ρ ΠΈ Ρ.Π΄.).
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## π¦ Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm install opencode-hashline
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## π§ ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ
|
|
163
|
+
|
|
164
|
+
ΠΠΎΠ±Π°Π²ΡΡΠ΅ ΠΏΠ»Π°Π³ΠΈΠ½ Π² Π²Π°Ρ `opencode.json`:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"$schema": "https://opencode.ai/config.json",
|
|
169
|
+
"plugin": ["opencode-hashline"]
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Π€Π°ΠΉΠ»Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
174
|
+
|
|
175
|
+
ΠΠ»Π°Π³ΠΈΠ½ Π·Π°Π³ΡΡΠΆΠ°Π΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΈΠ· ΡΠ»Π΅Π΄ΡΡΡΠΈΡ
ΠΌΠ΅ΡΡ (Π² ΠΏΠΎΡΡΠ΄ΠΊΠ΅ ΠΏΡΠΈΠΎΡΠΈΡΠ΅ΡΠ°, Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ·Π΄Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΠΈΡΡΠ²Π°ΡΡ ΡΠ°Π½Π½ΠΈΠ΅):
|
|
176
|
+
|
|
177
|
+
| ΠΡΠΈΠΎΡΠΈΡΠ΅Ρ | Π Π°ΡΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ | ΠΠ±Π»Π°ΡΡΡ |
|
|
178
|
+
|:---------:|-------------|---------|
|
|
179
|
+
| 1 | `~/.config/opencode/opencode-hashline.json` | ΠΠ»ΠΎΠ±Π°Π»ΡΠ½Π°Ρ (Π²ΡΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΡ) |
|
|
180
|
+
| 2 | `<project>/opencode-hashline.json` | ΠΠΎΠΊΠ°Π»ΡΠ½Π°Ρ (ΠΏΡΠΎΠ΅ΠΊΡ) |
|
|
181
|
+
| 3 | ΠΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½Π°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΡΠ΅ΡΠ΅Π· `createHashlinePlugin()` | ΠΡΠ³ΡΠΌΠ΅Π½Ρ ΡΠ°Π±ΡΠΈΠΊΠΈ |
|
|
182
|
+
|
|
183
|
+
ΠΡΠΈΠΌΠ΅Ρ `opencode-hashline.json`:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"exclude": ["**/node_modules/**", "**/*.min.js"],
|
|
188
|
+
"maxFileSize": 1048576,
|
|
189
|
+
"hashLength": 0,
|
|
190
|
+
"cacheSize": 100,
|
|
191
|
+
"prefix": "#HL "
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
ΠΠΎΡ ΠΈ Π²ΡΡ! ΠΠ»Π°Π³ΠΈΠ½ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ:
|
|
196
|
+
|
|
197
|
+
| # | ΠΠ΅ΠΉΡΡΠ²ΠΈΠ΅ | ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ |
|
|
198
|
+
|:-:|----------|----------|
|
|
199
|
+
| 1 | π **ΠΠ½Π½ΠΎΡΠΈΡΡΠ΅Ρ ΡΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»ΠΎΠ²** | ΠΡΠΈ ΡΡΠ΅Π½ΠΈΠΈ ΡΠ°ΠΉΠ»Π° AI ΠΊΠ°ΠΆΠ΄Π°Ρ ΡΡΡΠΎΠΊΠ° ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ `#HL` Ρ
Π΅Ρ-ΠΏΡΠ΅ΡΠΈΠΊΡ |
|
|
200
|
+
| 2 | π **ΠΠ½Π½ΠΎΡΠΈΡΡΠ΅Ρ `@file` ΡΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΡ** | Π€Π°ΠΉΠ»Ρ, ΠΏΡΠΈΠΊΡΠ΅ΠΏΠ»ΡΠ½Π½ΡΠ΅ ΡΠ΅ΡΠ΅Π· `@filename` Π² ΠΏΡΠΎΠΌΠΏΡΠ΅, ΡΠΎΠΆΠ΅ Π°Π½Π½ΠΎΡΠΈΡΡΡΡΡΡ Ρ
Π΅ΡΠ»Π°ΠΉΠ½Π°ΠΌΠΈ |
|
|
201
|
+
| 3 | βοΈ **Π£Π±ΠΈΡΠ°Π΅Ρ Ρ
Π΅Ρ-ΠΏΡΠ΅ΡΠΈΠΊΡΡ ΠΏΡΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ** | ΠΡΠΈ Π·Π°ΠΏΠΈΡΠΈ/ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΡΠ°ΠΉΠ»Π° Ρ
Π΅Ρ-ΠΏΡΠ΅ΡΠΈΠΊΡΡ ΡΠ΄Π°Π»ΡΡΡΡΡ ΠΏΠ΅ΡΠ΅Π΄ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ |
|
|
202
|
+
| 4 | π§ **ΠΠ½Π΅Π΄ΡΡΠ΅Ρ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΈ Π² ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ ΠΏΡΠΎΠΌΠΏΡ** | AI ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΈ ΠΏΠΎ ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠ°ΡΠΈΠΈ ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ hashline-ΡΡΡΠ»ΠΎΠΊ |
|
|
203
|
+
| 5 | πΎ **ΠΠ΅ΡΠΈΡΡΠ΅Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ** | ΠΠΎΠ²ΡΠΎΡΠ½ΡΠ΅ ΡΡΠ΅Π½ΠΈΡ ΡΠΎΠ³ΠΎ ΠΆΠ΅ ΡΠ°ΠΉΠ»Π° Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π°Π½Π½ΠΎΡΠ°ΡΠΈΠΈ |
|
|
204
|
+
| 6 | π **Π€ΠΈΠ»ΡΡΡΡΠ΅Ρ ΠΏΠΎ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ** | Π’ΠΎΠ»ΡΠΊΠΎ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ ΡΡΠ΅Π½ΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ `read_file`, `cat`, `view`) ΠΏΠΎΠ»ΡΡΠ°ΡΡ Π°Π½Π½ΠΎΡΠ°ΡΠΈΠΈ; ΠΎΡΡΠ°Π»ΡΠ½ΡΠ΅ Π½Π΅ Π·Π°ΡΡΠ°Π³ΠΈΠ²Π°ΡΡΡΡ |
|
|
205
|
+
| 7 | βοΈ **Π£ΡΠΈΡΡΠ²Π°Π΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ** | ΠΡΠΊΠ»ΡΡΡΠ½Π½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ ΠΈ ΡΠ°ΠΉΠ»Ρ, ΠΏΡΠ΅Π²ΡΡΠ°ΡΡΠΈΠ΅ `maxFileSize`, ΠΏΡΠΎΠΏΡΡΠΊΠ°ΡΡΡΡ |
|
|
206
|
+
| 8 | π§© **Π Π΅Π³ΠΈΡΡΡΠΈΡΡΠ΅Ρ `hashline_edit` tool** | ΠΡΠΈΠΌΠ΅Π½ΡΠ΅Ρ replace/delete/insert ΠΏΠΎ hash-ΡΡΡΠ»ΠΊΠ°ΠΌ Π±Π΅Π· ΡΠΎΡΠ½ΠΎΠ³ΠΎ `old_string`-ΠΌΠ°ΡΡΠΈΠ½Π³Π° |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## π οΈ ΠΠ°ΠΊ ΡΡΠΎ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ
|
|
211
|
+
|
|
212
|
+
### ΠΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Ρ
Π΅ΡΠ°
|
|
213
|
+
|
|
214
|
+
Π₯Π΅Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ Π²ΡΡΠΈΡΠ»ΡΠ΅ΡΡΡ ΠΈΠ·:
|
|
215
|
+
- **0-based ΠΈΠ½Π΄Π΅ΠΊΡΠ°** ΡΡΡΠΎΠΊΠΈ
|
|
216
|
+
- **Π‘ΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ ΡΡΡΠΎΠΊΠΈ** Ρ ΠΎΠ±ΡΠ΅Π·Π°Π½Π½ΡΠΌΠΈ Π·Π°Π²Π΅ΡΡΠ°ΡΡΠΈΠΌΠΈ ΠΏΡΠΎΠ±Π΅Π»Π°ΠΌΠΈ (trimEnd) β Π²Π΅Π΄ΡΡΠΈΠ΅ ΠΏΡΠΎΠ±Π΅Π»Ρ (ΠΎΡΡΡΡΠΏΡ) ΠΠΠΠ§ΠΠΠ«
|
|
217
|
+
|
|
218
|
+
ΠΡΠΎ ΠΏΠΎΠ΄Π°ΡΡΡΡ Π² Ρ
Π΅Ρ-ΡΡΠ½ΠΊΡΠΈΡ **FNV-1a**, ΡΠ²ΠΎΠ΄ΠΈΡΡΡ ΠΊ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠ΅ΠΌΡ ΠΌΠΎΠ΄ΡΠ»Ρ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΡΠ°ΠΉΠ»Π° ΠΈ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°Π΅ΡΡΡ ΠΊΠ°ΠΊ hex-ΡΡΡΠΎΠΊΠ°.
|
|
219
|
+
|
|
220
|
+
### Π₯ΡΠΊΠΈ ΠΈ tool ΠΏΠ»Π°Π³ΠΈΠ½Π°
|
|
221
|
+
|
|
222
|
+
ΠΠ»Π°Π³ΠΈΠ½ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅Ρ ΡΠ΅ΡΡΡΠ΅ Ρ
ΡΠΊΠ° OpenCode ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΊΠ°ΡΡΠΎΠΌΠ½ΡΠΉ tool:
|
|
223
|
+
|
|
224
|
+
| Π₯ΡΠΊ | ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ |
|
|
225
|
+
|-----|-----------|
|
|
226
|
+
| `tool.hashline_edit` | Hash-aware ΠΏΡΠ°Π²ΠΊΠΈ ΠΏΠΎ ΡΡΡΠ»ΠΊΠ°ΠΌ Π²ΡΠΎΠ΄Π΅ `5:a3f` ΠΈΠ»ΠΈ `#HL 5:a3f|...` |
|
|
227
|
+
| `tool.execute.after` | ΠΠΎΠ±Π°Π²Π»ΡΠ΅Ρ hashline-Π°Π½Π½ΠΎΡΠ°ΡΠΈΠΈ Π² Π²ΡΠ²ΠΎΠ΄ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΎΠ² ΡΡΠ΅Π½ΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² |
|
|
228
|
+
| `tool.execute.before` | Π£Π±ΠΈΡΠ°Π΅Ρ hashline-ΠΏΡΠ΅ΡΠΈΠΊΡΡ ΠΈΠ· Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ² ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΎΠ² ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ |
|
|
229
|
+
| `chat.message` | ΠΠ½Π½ΠΎΡΠΈΡΡΠ΅Ρ `@file` ΡΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΡ Π² ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡΡ
ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ (Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅Ρ Π°Π½Π½ΠΎΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΠΊΠΎΠ½ΡΠ΅Π½Ρ Π²ΠΎ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΉ ΡΠ°ΠΉΠ» ΠΈ ΠΏΠΎΠ΄ΠΌΠ΅Π½ΡΠ΅Ρ URL) |
|
|
230
|
+
| `experimental.chat.system.transform` | ΠΠΎΠ±Π°Π²Π»ΡΠ΅Ρ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΈ ΠΏΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ hashline Π² ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ ΠΏΡΠΎΠΌΠΏΡ |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## π ΠΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½ΡΠΉ API
|
|
235
|
+
|
|
236
|
+
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΡΡΠΈΠ»ΠΈΡΡ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΡΡΡΡ ΠΈΠ· ΡΡΠ±ΠΏΡΡΠΈ `opencode-hashline/utils` (ΡΡΠΎΠ±Ρ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ ΠΊΠΎΠ½ΡΠ»ΠΈΠΊΡΠΎΠ² Ρ Π·Π°Π³ΡΡΠ·ΡΠΈΠΊΠΎΠΌ ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ² OpenCode, ΠΊΠΎΡΠΎΡΡΠΉ Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠΊΡΠΏΠΎΡΡ ΠΊΠ°ΠΊ ΡΡΠ½ΠΊΡΠΈΡ Plugin):
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import {
|
|
240
|
+
computeLineHash,
|
|
241
|
+
formatFileWithHashes,
|
|
242
|
+
stripHashes,
|
|
243
|
+
parseHashRef,
|
|
244
|
+
normalizeHashRef,
|
|
245
|
+
buildHashMap,
|
|
246
|
+
getAdaptiveHashLength,
|
|
247
|
+
verifyHash,
|
|
248
|
+
resolveRange,
|
|
249
|
+
replaceRange,
|
|
250
|
+
applyHashEdit,
|
|
251
|
+
HashlineCache,
|
|
252
|
+
createHashline,
|
|
253
|
+
shouldExclude,
|
|
254
|
+
matchesGlob,
|
|
255
|
+
resolveConfig,
|
|
256
|
+
DEFAULT_PREFIX,
|
|
257
|
+
} from "opencode-hashline/utils";
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// ΠΡΡΠΈΡΠ»ΠΈΡΡ Ρ
Π΅Ρ Π΄Π»Ρ ΠΎΠ΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ
|
|
264
|
+
const hash = computeLineHash(0, "function hello() {"); // Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ "a3f"
|
|
265
|
+
|
|
266
|
+
// ΠΡΡΠΈΡΠ»ΠΈΡΡ Ρ
Π΅Ρ Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠΉ Π΄Π»ΠΈΠ½ΠΎΠΉ
|
|
267
|
+
const hash4 = computeLineHash(0, "function hello() {", 4); // Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ "a3f2"
|
|
268
|
+
|
|
269
|
+
// ΠΠ½Π½ΠΎΡΠΈΡΠΎΠ²Π°ΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ ΡΠ°ΠΉΠ»Π° (Π°Π΄Π°ΠΏΡΠΈΠ²Π½Π°Ρ Π΄Π»ΠΈΠ½Π° Ρ
Π΅ΡΠ°, Ρ ΠΏΡΠ΅ΡΠΈΠΊΡΠΎΠΌ #HL)
|
|
270
|
+
const annotated = formatFileWithHashes(fileContent);
|
|
271
|
+
// "#HL 1:a3|function hello() {\n#HL 2:f1| return \"world\";\n#HL 3:0e|}"
|
|
272
|
+
|
|
273
|
+
// ΠΠ½Π½ΠΎΡΠΈΡΠΎΠ²Π°ΡΡ Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠΉ Π΄Π»ΠΈΠ½ΠΎΠΉ Ρ
Π΅ΡΠ°
|
|
274
|
+
const annotated3 = formatFileWithHashes(fileContent, 3);
|
|
275
|
+
|
|
276
|
+
// ΠΠ½Π½ΠΎΡΠΈΡΠΎΠ²Π°ΡΡ Π±Π΅Π· ΠΏΡΠ΅ΡΠΈΠΊΡΠ° (legacy-ΡΠΎΡΠΌΠ°Ρ)
|
|
277
|
+
const annotatedLegacy = formatFileWithHashes(fileContent, undefined, false);
|
|
278
|
+
|
|
279
|
+
// Π£Π±ΡΠ°ΡΡ Π°Π½Π½ΠΎΡΠ°ΡΠΈΠΈ, ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅
|
|
280
|
+
const original = stripHashes(annotated);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Π₯Π΅Ρ-ΡΡΡΠ»ΠΊΠΈ ΠΈ Π²Π΅ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// Π Π°Π·ΠΎΠ±ΡΠ°ΡΡ Ρ
Π΅Ρ-ΡΡΡΠ»ΠΊΡ
|
|
287
|
+
const { line, hash } = parseHashRef("2:f1c"); // { line: 2, hash: "f1c" }
|
|
288
|
+
|
|
289
|
+
// ΠΠΎΡΠΌΠ°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ ΡΡΡΠ»ΠΊΡ ΠΈΠ· Π°Π½Π½ΠΎΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ
|
|
290
|
+
const ref = normalizeHashRef("#HL 2:f1c|const x = 1;"); // "2:f1c"
|
|
291
|
+
|
|
292
|
+
// ΠΠΎΡΡΡΠΎΠΈΡΡ ΠΊΠ°ΡΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠΉ
|
|
293
|
+
const map = buildHashMap(fileContent); // Map<"2:f1c", 2>
|
|
294
|
+
|
|
295
|
+
// ΠΠ΅ΡΠΈΡΠΈΡΠΈΡΠΎΠ²Π°ΡΡ Ρ
Π΅Ρ-ΡΡΡΠ»ΠΊΡ (ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ hash.length, Π° Π½Π΅ ΡΠ°Π·ΠΌΠ΅Ρ ΡΠ°ΠΉΠ»Π°)
|
|
296
|
+
const result = verifyHash(2, "f1c", fileContent);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Range-ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Π Π΅Π·ΠΎΠ»Π²ΠΈΡΡ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½
|
|
303
|
+
const range = resolveRange("1:a3f", "3:0e7", fileContent);
|
|
304
|
+
|
|
305
|
+
// ΠΠ°ΠΌΠ΅Π½ΠΈΡΡ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½
|
|
306
|
+
const newContent = replaceRange("1:a3f", "3:0e7", fileContent, "Π½ΠΎΠ²ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅");
|
|
307
|
+
|
|
308
|
+
// Hash-aware ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ (replace/delete/insert_before/insert_after)
|
|
309
|
+
const edited = applyHashEdit(
|
|
310
|
+
{ operation: "replace", startRef: "1:a3f", endRef: "3:0e7", replacement: "Π½ΠΎΠ²ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅" },
|
|
311
|
+
fileContent
|
|
312
|
+
).content;
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Π£ΡΠΈΠ»ΠΈΡΡ
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// ΠΡΠΎΠ²Π΅ΡΠΈΡΡ, Π½ΡΠΆΠ½ΠΎ Π»ΠΈ ΠΈΡΠΊΠ»ΡΡΠΈΡΡ ΡΠ°ΠΉΠ»
|
|
319
|
+
const excluded = shouldExclude("node_modules/foo.js", ["**/node_modules/**"]);
|
|
320
|
+
|
|
321
|
+
// Π‘ΠΎΠ·Π΄Π°ΡΡ Π½Π°ΡΡΡΠΎΠ΅Π½Π½ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ
|
|
322
|
+
const hl = createHashline({ cacheSize: 50, hashLength: 3 });
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## π ΠΠ΅Π½ΡΠΌΠ°ΡΠΊ
|
|
328
|
+
|
|
329
|
+
### ΠΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ: hashline vs str_replace
|
|
330
|
+
|
|
331
|
+
ΠΠ±Π° ΠΏΠΎΠ΄Ρ
ΠΎΠ΄Π° ΠΏΡΠΎΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½Ρ Π½Π° **60 ΡΠΈΠΊΡΡΡΡΠ°Ρ
ΠΈΠ· [react-edit-benchmark](https://github.com/can1357/oh-my-pi/tree/main/packages/react-edit-benchmark)** β ΠΌΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΡ
ΡΠ°ΠΉΠ»Π°Ρ
React Ρ ΠΈΠ·Π²Π΅ΡΡΠ½ΡΠΌΠΈ Π±Π°Π³Π°ΠΌΠΈ (ΠΈΠ½Π²Π΅ΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π±ΡΠ»Π΅Π²Ρ, ΠΏΠ΅ΡΠ΅ΠΏΡΡΠ°Π½Π½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΡ, ΡΠ΄Π°Π»ΡΠ½Π½ΡΠ΅ guard-ΠΊΠ»Π°ΡΠ·Ρ ΠΈ Ρ.Π΄.):
|
|
332
|
+
|
|
333
|
+
| | hashline | str_replace |
|
|
334
|
+
|---|:---:|:---:|
|
|
335
|
+
| **ΠΡΠΎΡΠ»ΠΎ** | **60/60 (100%)** | 58/60 (96.7%) |
|
|
336
|
+
| **ΠΡΠΎΠ²Π°Π»Π΅Π½ΠΎ** | 0 | 2 |
|
|
337
|
+
| **ΠΠ΅ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΡΠ΅ ΠΏΡΠ°Π²ΠΊΠΈ** | 0 | 4 |
|
|
338
|
+
|
|
339
|
+
str_replace Π»ΠΎΠΌΠ°Π΅ΡΡΡ, ΠΊΠΎΠ³Π΄Π° `old_string` Π²ΡΡΡΠ΅ΡΠ°Π΅ΡΡΡ Π² ΡΠ°ΠΉΠ»Π΅ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ°Π· (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠΈΠ΅ΡΡ guard-ΠΊΠ»Π°ΡΠ·Ρ, ΠΏΠΎΡ
ΠΎΠΆΠΈΠ΅ Π±Π»ΠΎΠΊΠΈ ΠΊΠΎΠ΄Π°). Hashline Π°Π΄ΡΠ΅ΡΡΠ΅Ρ ΠΊΠ°ΠΆΠ΄ΡΡ ΡΡΡΠΎΠΊΡ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎ ΡΠ΅ΡΠ΅Π· `lineNumber:hash`, ΠΏΠΎΡΡΠΎΠΌΡ Π½Π΅ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΠΎΡΡΡ ΠΈΡΠΊΠ»ΡΡΠ΅Π½Π°.
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# ΠΠ°ΠΏΡΡΡΠΈΡΠ΅ ΡΠ°ΠΌΠΈ:
|
|
343
|
+
npx tsx benchmark/run.ts # ΡΠ΅ΠΆΠΈΠΌ hashline
|
|
344
|
+
npx tsx benchmark/run.ts --no-hash # ΡΠ΅ΠΆΠΈΠΌ str_replace
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
<details>
|
|
348
|
+
<summary>ΠΡΠΈΠ±ΠΊΠΈ str_replace (ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡ structural)</summary>
|
|
349
|
+
|
|
350
|
+
- `structural-remove-early-return-001` β `old_string` ΡΠΎΠ²ΠΏΠ°Π» Π² Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ
ΠΌΠ΅ΡΡΠ°Ρ
, Π·Π°ΠΌΠ΅Π½Π° ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½Π° Π½Π΅ ΠΊ ΡΠΎΠΌΡ
|
|
351
|
+
- `structural-remove-early-return-002` β Π°Π½Π°Π»ΠΎΠ³ΠΈΡΠ½Π°Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°
|
|
352
|
+
- `structural-delete-statement-002` β Π½Π΅ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΠΎΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅ (ΠΏΠ΅ΡΠ²ΠΎΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅ ΠΎΠΊΠ°Π·Π°Π»ΠΎΡΡ Π²Π΅ΡΠ½ΡΠΌ)
|
|
353
|
+
- `structural-delete-statement-003` β Π½Π΅ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΠΎΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅ (ΠΏΠ΅ΡΠ²ΠΎΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅ ΠΎΠΊΠ°Π·Π°Π»ΠΎΡΡ Π²Π΅ΡΠ½ΡΠΌ)
|
|
354
|
+
|
|
355
|
+
</details>
|
|
356
|
+
|
|
357
|
+
### Π Π°ΡΡ
ΠΎΠ΄ ΡΠΎΠΊΠ΅Π½ΠΎΠ²
|
|
358
|
+
|
|
359
|
+
ΠΠ½Π½ΠΎΡΠ°ΡΠΈΠΈ hashline Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ ΠΏΡΠ΅ΡΠΈΠΊΡ `#HL <line>:<hash>|` (~12 ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² / ~3 ΡΠΎΠΊΠ΅Π½Π°) Π½Π° ΡΡΡΠΎΠΊΡ:
|
|
360
|
+
|
|
361
|
+
| | ΠΠ΅Π· Ρ
Π΅ΡΠ΅ΠΉ | Π‘ Ρ
Π΅ΡΠ°ΠΌΠΈ | ΠΠ²Π΅ΡΡ
Π΅Π΄ |
|
|
362
|
+
|---|---:|---:|:---:|
|
|
363
|
+
| **Π‘ΠΈΠΌΠ²ΠΎΠ»Ρ** | 404K | 564K | +40% |
|
|
364
|
+
| **Π’ΠΎΠΊΠ΅Π½Ρ (~)** | ~101K | ~141K | +40% |
|
|
365
|
+
|
|
366
|
+
ΠΠ²Π΅ΡΡ
Π΅Π΄ ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎ ~40% Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΠΎ ΠΎΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΡΠ°ΠΉΠ»Π°. ΠΠ»Ρ ΡΠΈΠΏΠΈΡΠ½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π° Π½Π° 200 ΡΡΡΠΎΠΊ (~800 ΡΠΎΠΊΠ΅Π½ΠΎΠ²) hashline Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅Ρ ~600 ΡΠΎΠΊΠ΅Π½ΠΎΠ² β ΠΏΡΠ΅Π½Π΅Π±ΡΠ΅ΠΆΠΈΠΌΠΎ ΠΌΠ°Π»ΠΎ ΠΏΡΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ½ΠΎΠΌ ΠΎΠΊΠ½Π΅ Π² 200K.
|
|
367
|
+
|
|
368
|
+
### ΠΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ
|
|
369
|
+
|
|
370
|
+
| Π Π°Π·ΠΌΠ΅Ρ ΡΠ°ΠΉΠ»Π° | ΠΠ½Π½ΠΎΡΠ°ΡΠΈΡ | ΠΡΠ°Π²ΠΊΠ° | Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Ρ
Π΅ΡΠ΅ΠΉ |
|
|
371
|
+
|-------------:|:---------:|:------:|:--------------:|
|
|
372
|
+
| **10** ΡΡΡΠΎΠΊ | 0.05 ΠΌΡ | 0.01 ΠΌΡ | 0.03 ΠΌΡ |
|
|
373
|
+
| **100** ΡΡΡΠΎΠΊ | 0.12 ΠΌΡ | 0.02 ΠΌΡ | 0.08 ΠΌΡ |
|
|
374
|
+
| **1 000** ΡΡΡΠΎΠΊ | 0.95 ΠΌΡ | 0.04 ΠΌΡ | 0.60 ΠΌΡ |
|
|
375
|
+
| **5 000** ΡΡΡΠΎΠΊ | 4.50 ΠΌΡ | 0.08 ΠΌΡ | 2.80 ΠΌΡ |
|
|
376
|
+
| **10 000** ΡΡΡΠΎΠΊ | 9.20 ΠΌΡ | 0.10 ΠΌΡ | 5.50 ΠΌΡ |
|
|
377
|
+
|
|
378
|
+
> Π’ΠΈΠΏΠΈΡΠ½ΡΠΉ ΡΠ°ΠΉΠ» ΠΈΠ· 1 000 ΡΡΡΠΎΠΊ Π°Π½Π½ΠΎΡΠΈΡΡΠ΅ΡΡΡ Π·Π° **< 1 ΠΌΡ** β Π½Π΅Π·Π°ΠΌΠ΅ΡΠ½ΠΎ Π΄Π»Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## π§βπ» Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ°
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ
|
|
386
|
+
npm install
|
|
387
|
+
|
|
388
|
+
# ΠΠ°ΠΏΡΡΡΠΈΡΡ ΡΠ΅ΡΡΡ
|
|
389
|
+
npm test
|
|
390
|
+
|
|
391
|
+
# Π‘ΠΎΠ±ΡΠ°ΡΡ
|
|
392
|
+
npm run build
|
|
393
|
+
|
|
394
|
+
# ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ²
|
|
395
|
+
npm run typecheck
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## π‘ ΠΠ΄ΠΎΡ
Π½ΠΎΠ²Π΅Π½ΠΈΠ΅ ΠΈ ΡΠ΅ΠΎΡΠ΅ΡΠΈΡΠ΅ΡΠΊΠ°Ρ Π±Π°Π·Π°
|
|
401
|
+
|
|
402
|
+
ΠΠ΄Π΅Ρ hashline Π²Π΄ΠΎΡ
Π½ΠΎΠ²Π»Π΅Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΏΡΠΈΡΠΌΠΈ ΠΈΠ· **oh-my-pi** ΠΎΡ [can1357](https://github.com/can1357/oh-my-pi) β AI-ΡΡΠ»ΠΊΠΈΡΠ° Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ (coding agent CLI, unified LLM API, TUI-Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ) β ΠΈ ΡΡΠ°ΡΡΠΈ Β«The Harness ProblemΒ» (ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΠΎΠ±Π²ΡΠ·ΠΊΠΈ).
|
|
403
|
+
|
|
404
|
+
**Π‘ΡΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ:** ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ AI-ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΎΠ±Π»Π°Π΄Π°ΡΡ ΠΎΠ³ΡΠΎΠΌΠ½ΡΠΌΠΈ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡΠΌΠΈ, Π½ΠΎ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ (harness), ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ ΠΈ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ Π΅Ρ ΠΏΡΠ°Π²ΠΊΠΈ ΠΊ ΡΠ°ΠΉΠ»Π°ΠΌ, ΡΠ΅ΡΡΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΈ ΠΏΠΎΡΠΎΠΆΠ΄Π°ΡΡ ΠΎΡΠΈΠ±ΠΊΠΈ. ΠΠΎΠ΄Π΅Π»Ρ Π²ΠΈΠ΄ΠΈΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ ΡΠ°ΠΉΠ»Π°, Π½ΠΎ ΠΏΡΠΈ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ Π²ΡΠ½ΡΠΆΠ΄Π΅Π½Π° Β«ΡΠ³Π°Π΄ΡΠ²Π°ΡΡΒ» ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ ΠΎΠΊΡΡΠΆΠ°ΡΡΠΈΡ
ΡΡΡΠΎΠΊ. Search-and-replace Π»ΠΎΠΌΠ°Π΅ΡΡΡ Π½Π° Π΄ΡΠ±Π»ΠΈΠΊΠ°ΡΠ°Ρ
ΡΡΡΠΎΠΊ, Π° diff-ΡΠΎΡΠΌΠ°Ρ ΡΠΎΠΆΠ΅ Π½Π΅Π½Π°Π΄ΡΠΆΠ΅Π½ Π½Π° ΠΏΡΠ°ΠΊΡΠΈΠΊΠ΅.
|
|
405
|
+
|
|
406
|
+
Hashline ΡΠ΅ΡΠ°Π΅Ρ ΡΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ, ΠΏΡΠΈΡΠ²Π°ΠΈΠ²Π°Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΡΡΡΠΎΠΊΠ΅ ΠΊΠΎΡΠΎΡΠΊΠΈΠΉ Π΄Π΅ΡΠ΅ΡΠΌΠΈΠ½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ Ρ
Π΅Ρ-ΡΠ΅Π³ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, `2:f1c`), ΡΡΠΎ Π΄Π΅Π»Π°Π΅Ρ Π°Π΄ΡΠ΅ΡΠ°ΡΠΈΡ ΡΡΡΠΎΠΊ **ΡΠΎΡΠ½ΠΎΠΉ ΠΈ ΠΎΠ΄Π½ΠΎΠ·Π½Π°ΡΠ½ΠΎΠΉ**. ΠΠΎΠ΄Π΅Π»Ρ ΠΌΠΎΠΆΠ΅Ρ ΡΡΡΠ»Π°ΡΡΡΡ Π½Π° Π»ΡΠ±ΡΡ ΡΡΡΠΎΠΊΡ ΠΈΠ»ΠΈ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ Π±Π΅Π· ΠΎΡΠΈΠ±ΠΎΠΊ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΈ ΠΏΡΡΠ°Π½ΠΈΡΡ Ρ Π΄ΡΠ±Π»ΠΈΠΊΠ°ΡΠ°ΠΌΠΈ.
|
|
407
|
+
|
|
408
|
+
**Π‘ΡΡΠ»ΠΊΠΈ:**
|
|
409
|
+
- [oh-my-pi ΠΎΡ can1357](https://github.com/can1357/oh-my-pi) β AI-ΡΡΠ»ΠΊΠΈΡ Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ: coding agent CLI, unified LLM API, TUI-Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ
|
|
410
|
+
- [The Harness Problem](https://blog.can.ac/2026/02/12/the-harness-problem/) β Π±Π»ΠΎΠ³-ΠΏΠΎΡΡ Ρ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠΌ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ΠΌ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
|
|
411
|
+
- [Π‘ΡΠ°ΡΡΡ Π½Π° Π₯Π°Π±ΡΠ΅](https://habr.com/ru/companies/bothub/news/995986/) β ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄Π° Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## π ΠΠΈΡΠ΅Π½Π·ΠΈΡ
|
|
416
|
+
|
|
417
|
+
[MIT](LICENSE) Β© opencode-hashline contributors
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatFileWithHashes,
|
|
3
|
+
getByteLength,
|
|
4
|
+
resolveConfig,
|
|
5
|
+
shouldExclude,
|
|
6
|
+
stripHashes
|
|
7
|
+
} from "./chunk-IVZSANZ4.js";
|
|
8
|
+
|
|
9
|
+
// src/hooks.ts
|
|
10
|
+
import { appendFileSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
var DEBUG_LOG = join(homedir(), ".config", "opencode", "hashline-debug.log");
|
|
14
|
+
function debug(...args) {
|
|
15
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
16
|
+
`;
|
|
17
|
+
try {
|
|
18
|
+
appendFileSync(DEBUG_LOG, line);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
var FILE_READ_TOOLS = ["read", "file_read", "read_file", "cat", "view"];
|
|
23
|
+
var FILE_EDIT_TOOLS = ["write", "file_write", "file_edit", "edit", "edit_file", "patch", "apply_patch", "multiedit"];
|
|
24
|
+
function isFileReadTool(toolName, args) {
|
|
25
|
+
const lower = toolName.toLowerCase();
|
|
26
|
+
const nameMatch = FILE_READ_TOOLS.some(
|
|
27
|
+
(name) => lower === name || lower.endsWith(`.${name}`)
|
|
28
|
+
);
|
|
29
|
+
if (nameMatch) return true;
|
|
30
|
+
if (args && typeof args === "object") {
|
|
31
|
+
if (typeof args.path === "string" || typeof args.filePath === "string" || typeof args.file === "string") {
|
|
32
|
+
const writeIndicators = ["write", "edit", "patch", "execute", "run", "command", "shell", "bash"];
|
|
33
|
+
const isWrite = writeIndicators.some((w) => lower.includes(w));
|
|
34
|
+
if (!isWrite) return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function createFileReadAfterHook(cache, config) {
|
|
40
|
+
const resolved = config ?? resolveConfig();
|
|
41
|
+
const hashLen = resolved.hashLength || 0;
|
|
42
|
+
const prefix = resolved.prefix;
|
|
43
|
+
return async (input, output) => {
|
|
44
|
+
debug("tool.execute.after:", input.tool, "args:", input.args);
|
|
45
|
+
if (!isFileReadTool(input.tool, input.args)) {
|
|
46
|
+
debug("skipped: not a file-read tool");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!output.output || typeof output.output !== "string") {
|
|
50
|
+
debug("skipped: no string output, type:", typeof output.output, "keys:", Object.keys(output));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const content = output.output;
|
|
54
|
+
if (resolved.maxFileSize > 0) {
|
|
55
|
+
const byteLength = getByteLength(content);
|
|
56
|
+
if (byteLength > resolved.maxFileSize) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const filePath = input.args?.path || input.args?.file || input.args?.filePath;
|
|
61
|
+
if (typeof filePath === "string" && shouldExclude(filePath, resolved.exclude)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (cache && typeof filePath === "string") {
|
|
65
|
+
const cached = cache.get(filePath, content);
|
|
66
|
+
if (cached) {
|
|
67
|
+
output.output = cached;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const annotated = formatFileWithHashes(content, hashLen || void 0, prefix);
|
|
72
|
+
output.output = annotated;
|
|
73
|
+
debug("annotated", typeof filePath === "string" ? filePath : input.tool, "lines:", content.split("\n").length);
|
|
74
|
+
if (cache && typeof filePath === "string") {
|
|
75
|
+
cache.set(filePath, content, annotated);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createFileEditBeforeHook(config) {
|
|
80
|
+
const resolved = config ?? resolveConfig();
|
|
81
|
+
const prefix = resolved.prefix;
|
|
82
|
+
return async (input, output) => {
|
|
83
|
+
const toolName = input.tool.toLowerCase();
|
|
84
|
+
const isFileEdit = FILE_EDIT_TOOLS.some(
|
|
85
|
+
(name) => toolName === name || toolName.endsWith(`.${name}`)
|
|
86
|
+
);
|
|
87
|
+
if (!isFileEdit) return;
|
|
88
|
+
if (!output.args || typeof output.args !== "object") return;
|
|
89
|
+
const contentFields = [
|
|
90
|
+
"content",
|
|
91
|
+
"new_content",
|
|
92
|
+
"old_content",
|
|
93
|
+
"old_string",
|
|
94
|
+
"new_string",
|
|
95
|
+
"replacement",
|
|
96
|
+
"text",
|
|
97
|
+
"diff",
|
|
98
|
+
"patch",
|
|
99
|
+
"patchText"
|
|
100
|
+
];
|
|
101
|
+
for (const field of contentFields) {
|
|
102
|
+
if (typeof output.args[field] === "string") {
|
|
103
|
+
output.args[field] = stripHashes(output.args[field], prefix);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function createSystemPromptHook(config) {
|
|
109
|
+
const resolved = config ?? resolveConfig();
|
|
110
|
+
const prefix = resolved.prefix === false ? "" : resolved.prefix;
|
|
111
|
+
return async (_input, output) => {
|
|
112
|
+
output.system.push(
|
|
113
|
+
[
|
|
114
|
+
"## Hashline \u2014 Line Reference System",
|
|
115
|
+
"",
|
|
116
|
+
`File contents are annotated with hashline prefixes in the format \`${prefix}<line>:<hash>|<content>\`.`,
|
|
117
|
+
"The hash length adapts to file size: 3 chars for files \u22644096 lines, 4 chars for larger files.",
|
|
118
|
+
"",
|
|
119
|
+
"### Example (small file, 3-char hashes):",
|
|
120
|
+
"```",
|
|
121
|
+
`${prefix}1:a3f|function hello() {`,
|
|
122
|
+
`${prefix}2:f1c| return "world";`,
|
|
123
|
+
`${prefix}3:0e7|}`,
|
|
124
|
+
"```",
|
|
125
|
+
"",
|
|
126
|
+
"### Example (large file, 4-char hashes):",
|
|
127
|
+
"```",
|
|
128
|
+
`${prefix}1:a3f2|import { useState } from 'react';`,
|
|
129
|
+
`${prefix}2:f12c|`,
|
|
130
|
+
`${prefix}3:0e7a|export function App() {`,
|
|
131
|
+
"```",
|
|
132
|
+
"",
|
|
133
|
+
"### How to reference lines:",
|
|
134
|
+
"You can reference specific lines using their hash tags (e.g., `2:f1c` or `2:f12c`).",
|
|
135
|
+
"When editing files, you may include or omit the hash prefixes \u2014 they will be stripped automatically.",
|
|
136
|
+
"",
|
|
137
|
+
"### Edit operations using hash references:",
|
|
138
|
+
"",
|
|
139
|
+
"**Preferred tool-based edit (hash-aware):**",
|
|
140
|
+
'- Use the `hashline_edit` tool with refs like `startRef: "2:f1c"` and optional `endRef`.',
|
|
141
|
+
"- This avoids fragile old_string matching because edits are resolved by hash references.",
|
|
142
|
+
"",
|
|
143
|
+
"**Replace a single line:**",
|
|
144
|
+
'- "Replace line 2:f1c" \u2014 target a specific line unambiguously',
|
|
145
|
+
"",
|
|
146
|
+
"**Replace a block of lines:**",
|
|
147
|
+
'- "Replace block from 1:a3f to 3:0e7" \u2014 replace a range of lines',
|
|
148
|
+
"- Example: replace lines 1:a3f through 3:0e7 with new content",
|
|
149
|
+
"",
|
|
150
|
+
"**Insert content:**",
|
|
151
|
+
'- "Insert after 3:0e7" \u2014 insert new lines after a specific line',
|
|
152
|
+
'- "Insert before 1:a3f" \u2014 insert new lines before a specific line',
|
|
153
|
+
"",
|
|
154
|
+
"**Delete lines:**",
|
|
155
|
+
'- "Delete lines from 2:f1c to 3:0e7" \u2014 remove a range of lines',
|
|
156
|
+
"",
|
|
157
|
+
"### Hash verification rules:",
|
|
158
|
+
"- **Always verify** that the hash reference matches the current line content before editing.",
|
|
159
|
+
"- If a hash doesn't match, the file may have changed since you last read it \u2014 re-read the file first.",
|
|
160
|
+
'- Hash references include both the line number AND the content hash, so `2:f1c` means "line 2 with hash f1c".',
|
|
161
|
+
"- If you see a mismatch, do NOT proceed with the edit \u2014 re-read the file to get fresh references.",
|
|
162
|
+
"",
|
|
163
|
+
"### Best practices:",
|
|
164
|
+
"- Use hash references for all edit operations to ensure precision.",
|
|
165
|
+
"- When making multiple edits, work from bottom to top to avoid line number shifts.",
|
|
166
|
+
"- For large replacements, use range references (e.g., `1:a3f to 10:b2c`) instead of individual lines."
|
|
167
|
+
].join("\n")
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export {
|
|
173
|
+
isFileReadTool,
|
|
174
|
+
createFileReadAfterHook,
|
|
175
|
+
createFileEditBeforeHook,
|
|
176
|
+
createSystemPromptHook
|
|
177
|
+
};
|