Line locator
Skill unkluco/line-locator
Agent skill for precise code navigation: locate functions fast, read less, save tokens.
npx -y skills add unkluco/line-locatorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Định vị dòng trước khi đọc file hoặc tìm file trong cây thư mục — tránh đọc mù, tiết kiệm token. Dùng khi cần tìm hàm, class, symbol, hoặc bất kỳ pattern nào trong source code trước khi gọi view_range. Hai công cụ: findtree.py (tìm file nào có chứa pattern) và findtool.py (tìm dòng nào trong một file).
SKILL.md
17.4 KB, as published. Nobody here has run it
Line-Locator
Hai script phối hợp để định vị chính xác trước khi đọc:
| Script | Phạm vi | Câu hỏi trả lời |
|---|---|---|
findtree.py | Cả cây thư mục | File nào chứa pattern này? |
findtool.py | Một file duy nhất | Dòng nào trong file này? |
Workflow:
findtree.py (shortlist files) → findtool.py (locate lines) → view_range (read)
Script nằm tại: skills/line-locator/scripts/
Output contract
Tất cả pattern dùng Python regex.
findtool — success (stdout):
{"matches": {"processOrder": [247, 312]}}
{"line": 248}
{"matched": true}
findtree — success (stdout):
{"matched_files": ["src/order.js", "tests/order.test.js"]}
Failure — cả hai tool (stderr, exit 1):
{"ok": false, "error": "No line matching pattern 'foo' was found after line 0."}
Parse nhanh:
import json, subprocess
out = subprocess.check_output([...])
data = json.loads(out)
# findtool
lines = data["matches"]["pat"] # -mr → list[int]
line = data["line"] # -n / -b / -c / -o → int
matched = data["matched"] # -e → bool
# findtree
files = data["matched_files"] # list[str] relative paths
findtree — tìm file
python findtree.py --root ROOT --pattern PAT [options]
| Option | Mặc định | Mô tả |
|---|---|---|
--root | (required) | Thư mục gốc |
--pattern | (required) | Regex pattern |
--include GLOB... | (tất cả) | Chỉ tìm file khớp glob |
--exclude GLOB... | (không) | Bỏ qua file/thư mục khớp glob |
--ignore-case | off | Case-insensitive |
--max-results N | (không giới hạn) | Dừng sau N file |
--show | matched | matched / errors / both / summary / all |
--max-file-size | (không) | Bỏ qua file lớn hơn (VD: 64K, 10M) |
--no-default-dir-excludes | off | Tắt auto-skip .git, node_modules, v.v. |
--text | off | Output text thay vì JSON |
Auto-excluded dirs: .git, .hg, node_modules, venv, .venv, __pycache__, dist, build, target, .mypy_cache, .pytest_cache, .tox, coverage
python findtree.py --root ./src --pattern "processOrder"
python findtree.py --root ./src --pattern "class\s+\w+Service"
python findtree.py --root . --pattern "TODO" --include "*.ts" "*.js" --exclude "dist/**"
python findtree.py --root . --pattern "deprecated_api" --max-results 1
findtool — tìm dòng
python findtool.py --file FILE [--ignore-case] [-s] FLAG... [--text]
| Flag | Args | Mô tả | result key |
|---|---|---|---|
-mr | PAT [PAT ...] | Tất cả dòng khớp một hoặc nhiều pattern | {"matches": {"pat": [1,5]}} |
-n | PAT LINE | Dòng đầu tiên khớp sau LINE (0 = từ đầu file) | {"line": 45} |
-b | PAT LINE | Dòng cuối cùng khớp trước LINE (999999 = cuối file) | {"line": 12} |
-e | PAT | File có chứa pattern không? | {"matched": true} |
-c | OPEN CLOSE LINE N | Dòng đóng khớp với OPEN thứ N trên LINE (scan xuôi) | {"line": 89} |
-o | OPEN CLOSE LINE N | Dòng mở khớp với CLOSE thứ N trên LINE (scan ngược) | {"line": 34} |
Flag -s (chỉ dùng với -c/-o): bỏ qua nội dung string và comment khi đếm depth.
python findtool.py --file app.js -mr "processOrder"
python findtool.py --file app.js -mr "processOrder" "cancelOrder" "getUser"
python findtool.py --file app.js -n "function\s+\w+" 0
python findtool.py --file app.js -b "^import\s" 999999
python findtool.py --file app.js -e "TODO"
python findtool.py --file app.js -c "\{" "\}" 248 1
python findtool.py --file app.js -c "\{" "\}" 248 1 -s
python findtool.py --file app.js -o "\{" "\}" 298 1
Pair matching (-c / -o)
Cả hai flag đều nhận OPEN CLOSE LINE N theo cùng thứ tự:
-c OPEN CLOSE LINE N → anchor tại OPEN thứ N trên LINE, scan xuôi → trả về dòng CLOSE
-o OPEN CLOSE LINE N → anchor tại CLOSE thứ N trên LINE, scan ngược → trả về dòng OPEN
Algorithm: depth-tracking. Mỗi dòng tiếp theo: depth += count(OPEN) - count(CLOSE). Khi depth == 0 → trả về dòng đó.
Flag -s: trước khi đếm, mask toàn bộ string literals và comments (//, #, /* */, '...', "...", backtick, triple-quote). Dùng khi file có nhiều delimiter trong string/comment.
Pairs phổ biến theo ngôn ngữ
| Ngôn ngữ | Block mở | Block đóng | Ghi chú |
|---|---|---|---|
| JS/TS/Java/Go/Rust/C/C++/C# | \{ | \} | Dùng -s nếu file có { trong string |
| Bất kỳ — argument list | \( | \) | Áp dụng mọi ngôn ngữ |
| Bất kỳ — array/list | \[ | \] | Áp dụng mọi ngôn ngữ |
| HTML/XML | <div\b[^>]*> | </div> | Thay div bằng tên tag |
| Ruby | \bdo\b | \bend\b | Hoặc \bdef\b / \bend\b |
| Lua / Pascal-like | \bdo\b | \bend\b | |
| Shell (bash) | \bthen\b|\bdo\b | \bfi\b|\bdone\b | Phức tạp hơn |
| Python | (không có bracket) | (xem note bên dưới) | Dùng -n tìm def tiếp theo |
Python note: Python dùng indentation thay vì
{}. Để tìm cuối hàm, dùng-n "^(def\|class)\s" FUNC_LINEtìmdef/classtiếp theo cùng cấp, hoặc đọc từFUNC_LINEđến dòng trước đó. Với argument list nhiều dòng:-c "\(" "\)" FUNC_LINE 1vẫn hoạt động.
# JS/TS function
python findtool.py --file service.ts -c "\{" "\}" 248 1 -s
# → {"line": 298}
# HTML div
python findtool.py --file index.html -c "<div\b[^>]*>" "</div>" 10 1
# → {"line": 25}
# Ruby method
python findtool.py --file user.rb -c "\bdef\b" "\bend\b" 42 1
# → {"line": 67}
# Argument list dài nhiều dòng (mọi ngôn ngữ)
python findtool.py --file builder.java -c "\(" "\)" 120 1
# → {"line": 128}
# -o: đang ở dòng }, tìm dòng {
python findtool.py --file app.go -o "\{" "\}" 298 1
# → {"line": 248}
Workflows
1. Đọc một hàm đã biết tên
# Bước 1: Tìm dòng định nghĩa
python findtool.py --file app.js -mr "processOrder"
# → {"matches": {"processOrder": [247, 312]}} → 247 là định nghĩa
# Bước 2: Tìm dòng OPEN thực sự (KHÔNG đoán cùng dòng hay dòng dưới)
python findtool.py --file app.js -n "\{" 247
# → {"line": 248}
# Bước 3: Tìm dòng đóng
python findtool.py --file app.js -c "\{" "\}" 248 1
# → {"line": 298}
# Bước 4: Đọc
view_range [247, 298]
2. Từ repository lạ → đọc hàm
python findtree.py --root ./src --pattern "processOrder"
# → {"matched_files": ["services/order.js"]}
python findtool.py --file services/order.js -mr "processOrder"
# → tiếp tục Workflow 1
3. Scan cấu trúc tổng thể file
# JS/TS/Java/Go/C#
python findtool.py --file service.ts -mr "class\s+\w+" "function\s+\w+" "\bexport\b"
# Python
python findtool.py --file service.py -mr "^class\s+\w+" "^def\s+\w+" "^ def\s+\w+"
# Ruby
python findtool.py --file user.rb -mr "^\s*def\s+\w+" "^\s*class\s+\w+"
# Java/C#
python findtool.py --file Service.java -mr "public\s+\w" "private\s+\w" "protected\s+\w"
4. Tìm nhiều symbol cùng lúc
python findtool.py --file app.js -mr "processOrder" "cancelOrder" "getUser"
# → {"matches": {"processOrder": [247], "cancelOrder": [312], "getUser": [89]}}
# Lên kế hoạch đọc toàn bộ từ một lần call
5. Đọc tất cả imports
# JS/TS
python findtool.py --file app.ts -mr "^import\s"
# → {"matches": {"^import\\s": [1,2,3,14,15]}} → view_range [1, 15]
# Python
python findtool.py --file app.py -mr "^import\s" "^from\s"
# Java/C#
python findtool.py --file App.java -mr "^import\s" "^using\s"
# Go
python findtool.py --file main.go -mr "^\s*\""
# (lines inside import block — hoặc tìm block import trước)
6. Xác định dòng đang thuộc scope nào (ngôn ngữ có {})
python findtool.py --file dao.js -b "\{" X # → OPEN_LINE
python findtool.py --file dao.js -c "\{" "\}" OPEN_LINE 1 # verify X trong scope này
view_range [OPEN_LINE - 1, OPEN_LINE] # đọc tên scope
7. Leo scope chain
python findtool.py --file app.js -b "\{" 245 → 198
python findtool.py --file app.js -b "\{" 198 → 134
python findtool.py --file app.js -b "\{" 134 → 12
python findtool.py --file app.js -b "\{" 12 → error → top-level
8. Tìm decorator / annotation
# Python decorator
python findtool.py --file routes.py -b "@\w+" FUNC_LINE
# → nếu kết quả >= FUNC_LINE - 3 → có decorator trực tiếp
# Java/C# annotation
python findtool.py --file Controller.java -b "@\w+" FUNC_LINE
# JS/TS decorator
python findtool.py --file service.ts -b "@\w+" FUNC_LINE
9. Tìm else / catch / finally
python findtool.py --file handler.js -c "\{" "\}" IF_LINE 1 # → close of if-block
python findtool.py --file handler.js -n "else|catch|finally" CLOSE_LINE
# → nếu ngay sau CLOSE_LINE → đây là else/catch của block đó
⚠️ Sai lầm phổ biến
1. Gọi -c trên dòng function name thay vì dòng {
# ❌ SAI — dòng 247 có tên hàm nhưng chưa chắc có {
python findtool.py --file app.js -c "\{" "\}" 247 1
# → error: "Line 247 contains only 0 occurrence(s) of open pattern '\{'"
# ✅ ĐÚNG — tìm dòng { trước, rồi mới -c
python findtool.py --file app.js -n "\{" 247 # → {"line": 248}
python findtool.py --file app.js -c "\{" "\}" 248 1
Lý do: Trong nhiều ngôn ngữ, { có thể ở dòng tiếp theo sau tên hàm (Allman style). Không bao giờ đoán.
2. Nhầm lẫn kết quả đầu tiên của -mr là định nghĩa
python findtool.py --file app.js -mr "processOrder"
# → {"matches": {"processOrder": [89, 247, 312]}}
# 89 = call site hoặc comment
# 247 = định nghĩa hàm (def/function)
# 312 = call site khác
Số nhỏ nhất chưa chắc là định nghĩa. Đọc thêm vài dòng context để xác nhận, hoặc dùng pattern cụ thể hơn:
python findtool.py --file app.js -mr "function processOrder" "processOrder\s*\("
# → pattern cụ thể hơn → ít kết quả giả hơn
3. Pattern có ký tự đặc biệt không escape
# ❌ SAI — regex error vì ( không được escape
python findtool.py --file app.py -mr "foo(bar)"
# → {"ok": false, "error": "Invalid regex pattern 'foo(bar)': missing ), ..."}
# ✅ ĐÚNG
python findtool.py --file app.py -mr "foo\(bar\)"
# hoặc dùng word boundary thay vì match toàn bộ call:
python findtool.py --file app.py -mr "foo"
Ký tự cần escape trong regex: . ^ $ * + ? { } [ ] \ | ( )
4. Dùng Python {} để match scope hàm
Python không dùng {} cho function/class body. Dùng -c "\{" "\}" sẽ chỉ match dict/set literals.
# ❌ Không có tác dụng cho Python function body
python findtool.py --file service.py -c "\{" "\}" 42 1
# ✅ Python: tìm def/class tiếp theo cùng level
python findtool.py --file service.py -n "^def \|^class " 42
# → dòng tiếp theo ở cùng indent level = kết thúc hàm hiện tại
# ✅ Python method trong class:
python findtool.py --file service.py -n "^ def " 42
5. Quên rằng -n là sau LINE, không phải tại LINE
python findtool.py --file app.js -n "def " 247
# → Tìm match STRICTLY AFTER dòng 247, không phải tại 247
# Nếu def ở dòng 247 → sẽ không được trả về
# Để bao gồm chính dòng đó:
python findtool.py --file app.js -n "def " 246 # tìm sau dòng 246
6. Bỏ qua -s khi file có nhiều { trong string/comment
# File có: const tmpl = `SELECT * FROM t WHERE col = '{value}'`
# ❌ Không -s: depth tracking đếm sai vì { trong template string
python findtool.py --file query.ts -c "\{" "\}" 50 1
# → kết quả sai
# ✅ Dùng -s để bỏ qua string/comment
python findtool.py --file query.ts -c "\{" "\}" 50 1 -s
Nên dùng -s khi: file là template (.html, .ejs, .hbs), SQL inline, hoặc bất kỳ file nào có nhiều { không phải code.
7. Dùng findtool trực tiếp mà không biết file ở đâu
# ❌ Tốn thời gian — không biết file nào
python findtool.py --file src/auth/service.js -mr "validateToken"
# → File not found
# ✅ findtree trước
python findtree.py --root ./src --pattern "validateToken"
# → {"matched_files": ["src/auth/service.js"]}
8. Không biết ngôn ngữ dùng { hay không → kiểm tra trước
Trước khi dùng -c "\{" "\}", xác nhận file có { không:
python findtool.py --file service.py -e "\{"
# → {"matched": false} → Python file, không có {-based scope
# → {"matched": true} → file có {, tiến hành bình thường
9. Dùng \| thay vì | cho alternation trong Python regex
# ❌ SAI — \| trong Python regex là ký tự pipe literal, không phải alternation
python findtool.py --file app.java -mr "focusLost\|hidePopup\|showPopup"
# → {"matches": {"focusLost\\|hidePopup\\|showPopup": []}} ← tìm chuỗi literal "focusLost|hidePopup|showPopup"
# ✅ ĐÚNG — | không cần escape trong Python regex
python findtool.py --file app.java -mr "focusLost|hidePopup|showPopup"
# → {"matches": {"focusLost|hidePopup|showPopup": [192, 723, 701]}}
Lý do nhầm: grep basic mode (BRE) dùng \| cho alternation. Python dùng ERE-style — | đã là alternation, \| là literal pipe.
Khi nào \| hợp lệ: không bao giờ trong Python regex nếu mục đích là alternation.
Bảng quyết định nhanh
| Tình huống | Dùng gì |
|---|---|
| Chưa biết file nào | findtree --root ... --pattern ... |
Biết tên hàm, muốn đọc body (ngôn ngữ có {}) | -mr "name" → -n "\{" LINE → -c "\{" "\}" OPEN 1 |
Ngôn ngữ không có {} (Python...) | -mr "name" → -n "^def|^class" LINE |
| Nhiều hàm cùng lúc | -mr "fn1" "fn2" "fn3" |
| Pattern có ký tự đặc biệt | escape: \(, \), \{, \., v.v. |
File có nhiều { trong string | thêm -s vào -c/-o |
| Duyệt từng hàm không biết tên | -mr "\{" → -c "\{" "\}" → -n "\{" → lặp |
| Ở dòng X, muốn biết thuộc hàm nào | -b "\{" X → -c "\{" "\}" OPEN 1 verify |
| Đọc hết imports | -mr "^import\s" → min/max → view_range |
| Argument list nhiều dòng | -c "\(" "\)" LINE 1 |
| Tìm else/catch/finally | -c "\{" "\}" IF_LINE 1 → -n "else|catch|finally" |
| Có decorator/annotation không? | -b "@\w+" FUNC_LINE → check khoảng cách |
| HTML tag pair | -c "<tag\b[^>]*>" "</tag>" LINE 1 |
| Ruby/Lua method body | -c "\bdef\b" "\bend\b" LINE 1 |
| Scope ngoài cùng | -b "\{" lặp đến error |
| File có chứa pattern không? | -e "pattern" |
| Chỉ tìm trong file .ts .js | findtree --include "*.ts" "*.js" |
| Dừng sớm khi tìm đủ | findtree --max-results 1 |
Xử lý lỗi
| Lỗi | Nguyên nhân | Cách xử lý |
|---|---|---|
"For -n, LINE must be >= 0" | Truyền số âm vào -n | Dùng 0 để tìm từ đầu file |
"For -b, LINE must be >= 1" | Truyền 0 vào -b | Dùng 999999 để tìm từ cuối file |
"Line X contains only 0 occurrence(s) of open pattern" | { không ở dòng đó | Dùng -n "\{" FUNC_LINE để tìm dòng { thực tế |
"No closing match for open pattern" | Không tìm thấy CLOSE trong file | File chưa đóng bracket, hoặc dùng thêm -s |
"No line matching pattern ... was found" | Pattern không khớp trong phạm vi | Kiểm tra lại regex; với -b có thể tăng LINE |
"Invalid regex pattern" | Regex sai cú pháp | Escape: \(, \), \{, \. |
"Root directory not found" | --root không tồn tại | Kiểm tra lại đường dẫn |
"N must be >= 1" | N = 0 trong -c/-o | N bắt đầu từ 1 |
Ví dụ end-to-end
Bài toán: Đọc hàm validateToken trong codebase 50 file, không biết nó nằm ở đâu.
# 1. Tìm file
python findtree.py --root ./src --pattern "validateToken"
# → {"matched_files": ["auth/service.ts"]}
# 2. Tìm dòng định nghĩa
python findtool.py --file auth/service.ts -mr "validateToken"
# → {"matches": {"validateToken": [312, 467]}} → 312 là định nghĩa
# 3. Kiểm tra decorator
python findtool.py --file auth/service.ts -b "@\w+" 312
# → {"line": 310} → có decorator ở dòng 310
# 4. Tìm dòng {
python findtool.py --file auth/service.ts -n "\{" 312
# → {"line": 313}
# 5. Tìm dòng đóng (dùng -s vì file TS hay có template string)
python findtool.py --file auth/service.ts -c "\{" "\}" 313 1 -s
# → {"line": 389}
# 6. Đọc
view_range [310, 389]
Token tiêu thụ: ~80 dòng thay vì hàng nghìn. Tiết kiệm >90%.