indexion-refactor

indexion-refactor

寫完程式後,偵測並清理三個層級的程式碼重複——複製貼上區塊、跨套件共享程式碼、不必要的包裝函式,以及概念層級的單一事實來源(SoT)違規。使用 indexion 偵測、修復並驗證。

1星標
2分支
更新於 2026/7/11
SKILL.md
唯讀
名稱
indexion-refactor
描述

寫完程式後,偵測並清理三個層級的程式碼重複——複製貼上區塊、跨套件共享程式碼、不必要的包裝函式,以及概念層級的單一事實來源(SoT)違規。使用 indexion 偵測、修復並驗證。

indexion refactor — 程式碼重構

使用 indexion 的分析指令,偵測並消除三個層級的重複——文字層級、結構層級與概念層級——然後驗證 SoT 是否被強制執行。

使用時機

  • 加入新的抽象(型別、模組、API 層)之後
  • 引入新的檔案格式或 I/O 邊界之後
  • 修復一個問題需要觸及 3 個以上檔案時
  • 為了繞過結構性問題而加入「防護」或「跳過」邏輯時
  • 從非預期路徑出現 opendirENOENT 或類似檔案系統錯誤時
  • 跨套件提取共享程式碼時
  • 重構後清理(移除瑣碎的包裝函式)時
  • 定期對程式碼庫進行 SoT 健康檢查

三個層級的重複

層級 說明 工具 範例
文字層級 複製貼上的程式碼區塊、完全相同的函式 plan refactor is_whitespace 在 5 個模組中被複製
結構層級 相同邏輯結構但不同名稱 plan solidplan unwrap 跨套件提取候選、瑣碎的包裝函式
概念層級 相同領域概念被獨立實作 explore + 人工分析 三個模組各自判斷「這個檔案是不是壓縮檔?」

文字層級重複容易發現與修復。概念層級重複最困難也最危險——它不會產生複製貼上的匹配,但修改一個概念需要更新所有分散的實作。

工作流程

階段 1:清除文字層級重複(plan refactor

從高信賴度的匹配開始,逐步降低門檻。

# 步驟 1:找出 90% 以上的重複(高信賴度)
indexion plan refactor --threshold=0.9 \
  --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' \
  cmd/indexion/

indexion plan refactor --threshold=0.9 \
  --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' \
  src/

將輸出分為三個部分閱讀:

部分 發現內容 處理方式
相似檔案 整體相似度高的檔案 調查是否需結構合併
重複程式碼區塊 檔案間行層級完全相同的程式碼 提取至 @common 或共享模組
函式層級重複 結構相似的函式(對函式主體做 TF-IDF) 統一為單一 SoT 函式

同檔案內的重複(同一檔案內相似度 90% 以上的函式)是最高價值的目標——最容易修復,效益最明確。例如:get_global_data_dirget_global_cache_dir 共享 95% 的結構,可提取為 resolve_os_dir

# 步驟 2:在合併前使用 grep 追蹤參考
indexion grep "TypeIdent:TfidfEmbeddingProvider" src/
indexion grep --semantic=name:is_whitespace src/

# 步驟 3:修復,然後重新執行以確認重複已消失
indexion plan refactor --threshold=0.9 --include='*.mbt' ...

# 步驟 4:降低門檻並迭代
indexion plan refactor --threshold=0.85 --include='*.mbt' ...

plan refactor 選項:

選項 預設值 說明
--threshold=FLOAT 0.7 最小相似度門檻
--strategy=NAME hybrid 相似度演算法:hybrid、tfidf、bm25、jsd、ncd
--fdr=FLOAT 0 FDR 校正(0=停用)
--style=STYLE raw 輸出格式:raw、structured
--format=FORMAT md 輸出格式:md、json、text、github-issue
--name=NAME -- 專案名稱(用於 structured 風格)
--include=PATTERN -- 包含模式(可重複)
--exclude=PATTERN -- 排除模式(可重複)
-o, --output=FILE stdout 輸出檔案路徑
--specs-dir=DIR kgfs KGF 規格目錄

清理後仍保留的內容(停止訊號):

  • 平台樁程式native.mbt / stub.mbt)——有意的平台分支
  • 型別方法相似性(不同型別的 to_string)——不同型別,相同模式
  • CLI 指令樣板command() 函式)——@argparse API 模式,非重複
  • 語義不同但相似的函式(is_disqualifying_keyword vs is_skip_token)——目的不同

階段 2:提取跨套件共享程式碼(plan solid

在清理每個目錄內部的重複後,找出應跨套件共享的程式碼。

# 找出兩個套件之間的重疊
indexion plan solid --from=src/a,src/b

# 指定提取目標
indexion plan solid --from=src/a,src/b --to=src/common

# 使用樹編輯距離進行精確的函式層級匹配
indexion plan solid --from=src/a,src/b --strategy=apted

# 使用較高門檻進行更嚴格的匹配
indexion plan solid --from=src/a,src/b --threshold=0.95

# 過濾檔案
indexion plan solid --from=src/a,src/b --include='*.mbt' --exclude='*_test.mbt'

plan solidplan refactor 的差異:

plan refactor plan solid
範圍 目錄內部的重複 跨目錄的重疊
目標 合併程式碼庫內部的重複 將共享程式碼提取到新套件
輸入 <path> --from=dirA,dirB

plan solid 選項:

選項 預設值 說明
--from=DIRS (必填) 來源目錄(逗號分隔或可重複)
--to=DIR -- 提取目標目錄
--rules=FILE -- 規則檔案(.solidrc)
--rule=RULE -- 內聯規則(可重複)
--threshold=FLOAT 0.9 最小相似度門檻
--strategy=NAME tfidf 相似度演算法:tfidf、apted、tsed
--include=PATTERN -- 包含模式(可重複)
--exclude=PATTERN -- 排除模式(可重複)
--format=FORMAT md 輸出格式:md、json、github-issue
-o, --output=FILE stdout 輸出檔案路徑
--specs-dir=DIR kgfs KGF 規格目錄

工作流程:

  1. 先對每個目錄個別執行 plan refactor 清理內部重複
  2. 執行 plan solid --from=dirA,dirB 找出跨目錄的提取候選
  3. 依照計畫的建議提取共享程式碼
  4. 使用 indexion grep "TypeIdent:SharedType" 驗證所有參考都已更新

階段 3:移除不必要的包裝函式(plan unwrap

在合併完成後,清理那些只增加間接層而無價值的瑣碎委派函式。

# 步驟 1:快速檢查
indexion grep --semantic=proxy src/

# 步驟 2:詳細報告
indexion plan unwrap --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' src/

# 步驟 3:預覽變更(安全——不修改檔案)
indexion plan unwrap --dry-run --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' src/

# 步驟 4:套用修復
indexion plan unwrap --fix --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' src/

# 步驟 5:執行測試
moon test --target native

會被偵測的內容: 函式主體只有單一函式呼叫,且所有參數都以簡單識別碼轉發——沒有控制流程,沒有轉換。

// 被偵測(預設)——瑣碎委派
fn matches_pattern(text : String, pat : String) -> Bool {
  @glob.glob_match(text, pat)
}

// 預設排除(使用 --all 包含)
fn length(self : MyList) -> Int {
  self.items.length()    // 自我委派(封裝)
}
fn emit(value : String) -> Action {
  Emit(value)            // 裸建構子
}

plan unwrap 模式:

模式 旗標 說明
報告 (預設) 列出找到的包裝函式
預覽 --dry-run 顯示所有編輯而不修改檔案
修復 --fix 將編輯套用到檔案

plan unwrap 選項:

選項 預設值 說明
--dry-run -- 預覽編輯
--fix -- 套用編輯
--all -- 包含自我委派與裸建構子包裝函式
--include-self -- 包含 self.field.method 模式
--include-bare -- 包含裸建構子包裝函式
--include=PATTERN -- 包含模式(可重複)
--exclude=PATTERN -- 排除模式(可重複)
--format=FORMAT md 輸出格式:md、json、text
-o, --output=FILE stdout 輸出檔案路徑
--specs-dir=DIR kgfs KGF 規格目錄

移除前請審查:

  • 平台包裝函式(FFI、@osenv_path)是抽象層,非意外間接層
  • 被外部套件使用的公開 API 包裝函式——移除它們會造成破壞性變更
  • 務必先執行 --dry-run

階段 4:偵測概念層級重複(explore + 分析)

這是最困難的層級。文字與結構工具無法找到它,因為程式碼不同——但概念相同。

# 找出哪些檔案共享詞彙(= 在同一個概念領域中運作)
indexion explore --threshold=0.4 \
  --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' \
  src/ cmd/

相似度在 40-60% 且無結構重複的檔案是概念鄰居——它們使用相同詞彙是因為處理相同領域。

對於每個高相似度配對,問:「它們共享什麼概念,誰擁有它?」

# 使用樹結構比較檢查共享詞彙
indexion explore file_a.mbt file_b.mbt --threshold=0 --strategy=apted

常見的概念洩漏模式:

症狀 洩漏的概念 修復方式
兩個檔案都呼叫 is_X(spec) 然後 Y::from_spec(spec) 「判斷是否為 X 並設定 Y」 try_do_X(path, spec) 提取到擁有 X 的模組
兩個檔案都在內容已載入時呼叫 @fs.read_file_to_string(path) 「讀取檔案內容」 將內容作為參數傳遞,不要重新讀取
兩個檔案都執行 parent_dir(path) 然後 @fs.read_dir(dir) 「列出同層檔案」 將目錄走訪集中到管線中
多個 if is_virtual_path(x) { skip } 防護 「真實 vs 虛擬路徑」 讓型別系統防止虛擬路徑到達此處
兩個檔案都執行 buf.write_string("\n"); buf.write_string(x) 「連接文字條目」 join_text_entries() 提取到擁有模組

階段 5:合併到 SoT

定義概念的模組應該是唯一實作邏輯的模組。

規則:

  1. 一個概念,一個模組,一個函式。 如果「從壓縮檔提取文字」出現在 vfs.mbtdiscover.mbtargs.mbt,它只應屬於 vfs.mbt
  2. 呼叫者接收結果,而非原料。 不要分別匯出 is_archive_spec + ArchiveSpec::from_spec + expand_archive。匯出 try_extract_archive_text(path, spec) -> String?
  3. 防護是症狀,不是修復。 if is_virtual_path(x) { skip } 表示虛擬路徑根本不該到達此處。修復來源,而非接收端。
  4. 從磁碟重新讀取已在記憶體中的內容是概念洩漏。 如果 SupportedFile.content 已持有文字,下游程式碼不應呼叫 @fs.read_file_to_string(file.path)

階段 6:驗證

# 確認文字層級重複已消失
indexion plan refactor --threshold=0.9 \
  --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' \
  src/ cmd/indexion/

# 確認概念相似度已降低
indexion explore file_a.mbt file_b.mbt --threshold=0

# 確認包裝函式已清理
indexion plan unwrap --include='*.mbt' --exclude='*_wbtest.mbt' \
  --exclude='*moon.pkg*' --exclude='*pkg.generated*' src/

# 執行測試
moon test --target native

SoT 合併後:

  • 概念擁有者與其呼叫者之間的文字相似度下降
  • 呼叫者變得更短(單一 API 呼叫取代多步驟邏輯)
  • 概念擁有者可能變大,但它是唯一需要修改的地方

階段 7:用測試證明不再復發

撰寫一個從結構上防止舊模式復發的測試:

test "SoT: SupportedFile.path 永遠是真實的檔案系統路徑" {
  // 建立壓縮檔,執行 load_supported_file_info
  // 斷言:沒有路徑包含 "!/"
  // 斷言:每個路徑都通過 @fs.path_exists
}

測試不檢查行為——它檢查的是 SoT 不變量

紅旗

「我需要在這裡加入一個防護」

如果你正在對一個不該接收特殊情況的函式加入 if is_special_case(x) { skip }問題在上游。該函式的呼叫者永遠不該傳入那個值。

「它運作正常,但會將錯誤輸出到 stderr」

來自 C 執行時的 stderr 訊息(opendir: No such file or directory)表示無效資料到達了系統呼叫。catch 吸收了錯誤,但 perror() 已經輸出了。唯一的修復方式是防止無效資料到達該呼叫。

「我會在每個指令中分別修復」

如果相同的修復需要在 explore、search、grep、reconcile、plan documentation 中進行……修復應屬於共享管線,而非每個指令。

「相似度只是共享詞彙,不是真正的重複」

在不該共享概念的模組之間出現 40-60% 的 TF-IDF 相似度是一個警告。詞彙匹配本身就是訊號。

快速參考:何時使用哪個指令

問題 指令
「哪些檔案相似?」 explore --format=list
「到底什麼被重複了?」 plan refactor --threshold=0.9
「套件 A 和 B 之間有哪些程式碼重疊?」 plan solid --from=A,B
「哪些函式是瑣碎的包裝函式?」 plan unwrapgrep --semantic=proxy
「這些檔案共享什麼概念?」 explore file_a file_b --threshold=0 --strategy=apted
「重複被修復了嗎?」 以相同門檻重新執行 plan refactor