@seanmars/tospec 0.14.0 → 0.14.2
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/CHANGELOG.md +38 -0
- package/README.md +9 -0
- package/assets/metrics/app.js +995 -0
- package/assets/metrics/chart.umd.js +14 -0
- package/assets/metrics/index.html +14 -0
- package/assets/metrics/style.css +193 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/dashboard.d.ts +0 -6
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +7 -120
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/metrics.d.ts +145 -0
- package/dist/commands/metrics.d.ts.map +1 -0
- package/dist/commands/metrics.js +380 -0
- package/dist/commands/metrics.js.map +1 -0
- package/dist/core/codex-metrics.d.ts +95 -0
- package/dist/core/codex-metrics.d.ts.map +1 -0
- package/dist/core/codex-metrics.js +293 -0
- package/dist/core/codex-metrics.js.map +1 -0
- package/dist/core/local-server.d.ts +67 -0
- package/dist/core/local-server.d.ts.map +1 -0
- package/dist/core/local-server.js +169 -0
- package/dist/core/local-server.js.map +1 -0
- package/dist/core/skill-metrics.d.ts +185 -0
- package/dist/core/skill-metrics.d.ts.map +1 -0
- package/dist/core/skill-metrics.js +321 -0
- package/dist/core/skill-metrics.js.map +1 -0
- package/package.json +2 -9
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
tospec 是一套 spec-driven development CLI: 以 schema 定義文件結構與工作流程, 進度由檔案系統狀態推算, 開發方法則封裝於 Skill 之中, 使 AI 工具 (Claude Code / Codex) 得以循序完成需求釐清、規格撰寫、設計、任務拆解、實作到歸檔的完整流程.
|
|
8
8
|
|
|
9
|
+
## [0.14.2] - 2026-07-31
|
|
10
|
+
|
|
11
|
+
本版本新增 `tospec skill-metrics`: 一份以本機網頁呈現的耗時報告, 回答「哪個 Skill 最花時間、哪個最不穩定、工作實際發生在哪些日子」. 資料同時取自 Claude Code 與 Codex, 但兩種來源的歸屬精準度不同, 因此在任何位置都不合併為單一數字. 為此將 dashboard 的本機伺服器基礎設施抽離為共用模組 — 其中兩項是信任邊界, 複製一份等同於製造一個修好第一份也不會消失的漏洞.
|
|
12
|
+
|
|
13
|
+
### 新增
|
|
14
|
+
|
|
15
|
+
- **`tospec skill-metrics`: 以本機網頁圖表呈現每個 Skill 的實際耗時**: tospec 產生的 Skill 各自耗費多少時間, 先前無從得知 — 資料一直都在 agent 的 transcript 之中, 但「哪個 Skill 最花時間」「哪個最不穩定」這類問題需要跨執行比較, 人工翻閱無法回答. 本指令解析 transcript, 推算每次執行的 span (起訖區間) 與 engaged (實際投入時間), 於 loopback 啟動伺服器、印出網址並開啟瀏覽器, 停留在前景直到 Ctrl+C. 連接埠自 **26693** 起, 被占用即向上遞增, 因此重複開啟不會失敗; 這是刻意推翻 ADR 中原本 `port: 0` 交由作業系統指派的寫法 — 兩者都能避開衝突, 差別在於指派的連接埠每次都不同, 網址無法加入書籤、留在分頁中, 也無從在多次執行之間辨認. metrics 不具備 dashboard 的 registry、pid 檔、`--stop` 與「一專案一實例」規則: 那些機制的存在前提是「常駐服務必須能被其他指令再次找到」, 而 metrics 是一個開啟、看完就關掉的頁面, 向上遞增即為衝突處理的全部.
|
|
16
|
+
- **網頁是唯一的輸出形式, 不提供資料類 flag**: `--json`、`--runs`、`--since`、`--idle-gap`、`--split-gap` 皆不存在, 僅保留 `--no-open` (只印網址, 供無瀏覽器的環境使用). 時間範圍等三項參數在頁面上都有對應控制項, 而頁面的時間範圍是一次伺服器往返, 命令列給定的初始值會被第一次互動覆寫; 只保留其中兩個則需要在「瀏覽參數」與「演算法調校參數」之間劃出一條設計中別處都不存在的界線. `--no-open` 留下是因為它不是資料參數, 也沒有頁面等價物 — 沒有任何控制項能取消一個已經開啟的瀏覽器. flag 移除後, `localStorage` 成為偏好設定的唯一通道, 故控制項的選擇與主題、語言一併記憶.
|
|
17
|
+
- **五張圖, 每一種編碼都以真實 transcript 驗證過**: 總投入時間 (實色為 engaged, 低對比部分為 idle, 整根長度即 span)、典型單次時長、每次執行的分佈 (每次 run 一個點)、每日投入時間, 以及執行時間軸. 總量與中位數刻意分成兩張: 兩者回答不同問題, 且在實測資料上彼此不一致 (apply 的總 engaged 時間最高, explore 的中位數最高); 若依原案將中位數併入分佈圖作為標記, 此一比較就從「看得出來」退化為「必須自行推算」. 兩個中位數採並列而非堆疊 — 堆疊會把 `median(span) - median(engaged)` 呈現為「中位數 idle 時間」, 但兩個中位數的差並不是差值的中位數.
|
|
18
|
+
- **三種在紙上看似正確的編碼被資料否決**: (1) engaged 與 idle 獨立成圖會是一張近半數長條沒有第二段的堆疊圖 — idle 僅計入單次執行內超過門檻的停頓, 所有執行都短於門檻的 Skill 在結構上不可能累積 idle; 改為併入總量圖. (2) span 與 engaged 的分佈以每個 Skill 兩根長條並列, 二十二根中會有十根完全相同, 原因同上; 改為疊合於同一列. (3) 每日圖以 UTC 分桶會使三十六次執行中的十一次落在錯誤的日期, 因為在 UTC+8 當地 08:00 之前的執行都會被歸入前一個 UTC 日; 改以當地日期分桶, 每次執行整筆計入其起始日.
|
|
19
|
+
- **一層可丟棄的解析快取, 而非無狀態伺服器**: transcript 於啟動時解析一次並保留, 控制項變更只重跑純函式 `buildRuns` + `aggregateRuns`, 頁面的 Refresh 才丟棄快取並重新讀取磁碟. 兩個動作因此具有誠實不同的成本, Refresh 也才有存在的理由 — transcript 在頁面開啟期間持續增長, 包含正在檢視該頁面的那個 session. 伺服器組合 `skill-metrics.ts` 既有的匯出純函式, 而非呼叫其便利包裝 `collectSkillMetrics` (後者在同一趟中完成讀取與計算, 兩者之間沒有可供快取的接點), 代價是約三十行的組裝邏輯重寫, 換得該模組與其二十個測試完全不需更動.
|
|
20
|
+
- **不具備遠端表面**: metrics 沒有 `--host`、`--allow-remote` 或 `--port`, 一律綁定 loopback, dashboard 的 `--allow-remote` opt-in 在此無物可管. Host header 允許清單仍然適用 — 僅綁定 loopback 並不能阻止惡意頁面將自己的網域解析至 127.0.0.1. 決策記錄: `tospec/decisions/20260730_205609-metrics-web-only-output.md`.
|
|
21
|
+
- **同時自 Codex 的 session 推算耗時, 但兩種來源絕不相加**: Claude Code 與 Codex 收到的是同一套產生出來的 Skill, 報告卻只讀取 Claude Code 的 transcript, 在兩邊分工的使用者眼中就是一份不完整、卻沒有標示為不完整的答案. 兩者的差異不只在路徑: Claude Code 每個專案一個目錄, 列出該目錄本身即為專案過濾; Codex 則是所有專案共用一棵依日期分層的樹, 工作目錄記錄在每個 session 檔案之內, 因此劃定範圍意味著開啟檔案而非解析路徑. 更關鍵的是 Codex 沒有 `attributionSkill` 的對應欄位 — 每個 session 的 system prompt 都會完整列出所有已安裝的 Skill, 不論其中是否有任何一個真的執行過, 該清單因而毫無鑑別力; 唯一與「某個 Skill 確實被使用」相關的事件, 是一次讀取結尾為 `<skill>/SKILL.md` 之路徑的 tool call. Codex 的歸屬因此是一個結構上必然低估的推論 (從未重新讀取自身指示的執行完全不可見), 所以 `source` 是每一筆 run 與每一個彙總的必要欄位, 彙總以 `(skill, source)` 分組, 兩種來源在任何位置都不相加. 每一列的標籤都標上來源工具, 而非只標 Codex: 只標其中一方會使 Claude Code 成為未加註的預設值, 該列會被讀成「這個 Skill」而不是「某個 agent 對它的量測」, 而後者正是「兩者永不相加」得以被理解的前提.
|
|
22
|
+
- **以 64 KiB 前綴探測 cwd, 而非完整解析整棵樹**: 由於過濾鍵位在檔案之內, 第一版實作會讀取並 `JSON.parse` 機器上每一個 session 的每一行, 事後才丟棄屬於其他專案的部分. 實測該共用樹有 94 個 session 共 716 MB, 本專案僅占 0.89 MB (0.12%), 啟動時間自 Claude Code 的 139 ms 膨脹至 9811 ms, 其中 91% 花在解析其他專案的 session. `session_meta` 在取樣的 94 個檔案中皆位於第 0 行, 故改為自 offset 0 讀取上限 64 KiB, 自其中取回記錄的 cwd, 僅對相符者完整解析: 9.8 s 降為 35 ms, 且解析結果是與舊的完整走訪逐位元組比對, 而非抽樣檢查. 64 KiB 是正確性下限而非可調參數 — 探測範圍必須容納完整的一行, 被讀取邊界切斷的行會使 `JSON.parse` 失敗, 且與「該記錄不存在」無法區分; 探測結果不確定時回退為完整讀取, 因此形狀異常的 session 只會變慢, 不會被歸錯. 以 regex 掃描原始前綴可容許更小的探測範圍, 但等同於用模式比對重新實作 JSON unescaping, Codex 一旦更動 escaping 即靜默失效; 併行讀取則是針對錯誤的量 — 它仍然解析全部 716 MB, 而主導成本是無法重疊的 CPU 工作. 此項亦是啟動快取的前提: 快取無法分攤一筆在網址印出「之前」就付掉的成本, 頁面的 Refresh 更會再付一次. 同一套走訪原本存在兩份 (`collectCodexSkillMetrics` 與 `readCodexTranscripts`), 各自帶有 glob、讀取、解析與過濾的副本, 任何修正都可能只套用到其中一份; 現收斂為單一實作. 決策記錄: `tospec/decisions/20260731_153314-codex-session-cwd-prefix-probe.md`.
|
|
23
|
+
|
|
24
|
+
### 變更
|
|
25
|
+
|
|
26
|
+
- **dashboard 的本機伺服器基礎設施抽離為 `src/core/local-server.ts`**: JSON 回應、靜態資源服務、listen helper、Host header 允許清單與瀏覽器開啟指令原本全是 dashboard 的私有實作, 第二個本機指令只能靠複製取得. 其中兩項是信任邊界, 這使複製不只是不整潔, 而是不可接受: `isAllowedHostHeader` 是 DNS rebinding 防護, `serveAsset` 是路徑逃逸防護, 任一項的第二份實作都是一個「修好第一份也不會消失」的漏洞. 共用範圍刻意不對稱 — 安全關鍵的伺服器層共用, 前端資源不共用 (版面與圖表不具同類風險, 故 `assets/metrics/` 完全獨立). 兩項配合調整使單一實作足以服務兩個呼叫端: `serveAsset` 改以參數接收 root 目錄, 由 `packageAssetsDir` 單一負責「套件根目錄在上幾層」— 該深度是有作用的, assets 以 package `files` 出貨且從不編譯, 模組深度錯誤會解析到套件之外, 編譯至 `dist/core/` 後往上兩層仍是套件根, 與 `dist/commands/` 的答案相同; EADDRINUSE 的向上遞增改為 `listenFrom` 並回傳實際綁定的連接埠, 使「遞增直到綁定成功」只有一份實作. 未採「自 dashboard 指令模組匯出這些函式」的做法 — 那會將 markdown 渲染、dashboard 資料收集與執行中實例的 registry 這整條相依鏈, 拖進任何只想要一個 JSON responder 的指令. dashboard 側無可觀察的行為變更, 既有的 server、detach、stop 與 style 測試即為此次搬移的防護. 決策記錄: `tospec/decisions/20260730_205517-metrics-own-foreground-server.md`.
|
|
27
|
+
|
|
28
|
+
### 其他
|
|
29
|
+
|
|
30
|
+
- **指令定名為 `tospec skill-metrics`**: `tospec metrics` 讀起來像是涵蓋一般性的 metrics — 專案統計、CLI 使用量, 任何可計數的東西 — 但這份報告從頭到尾只講一件事: 每個 Skill 花多少時間. 該名稱會引導人去找一個不存在的東西, 同時錯過真正存在的那個. 現與自實作之初即使用正確名稱的 `src/core/skill-metrics.ts` 一致, 行為、flag 與輸出皆未變動. 頁面在瀏覽器 localStorage 的偏好設定鍵維持 `tospec-metrics`: 它不是使用者可見的名稱, 更名只會靜默丟棄每個人已儲存的主題、語言與門檻設定.
|
|
31
|
+
- **本版開發過程中的介面反覆未構成對外的 breaking change**: `tospec metrics` 的文字表格版本、其五個資料類 flag, 以及後續的更名, 全部發生在 0.14.0 與 0.14.2 之間, 從未出現於任何已發行版本. 對使用者而言本版是 `tospec skill-metrics` 首次出現, 而非既有指令的變更; commit 與 ADR 中的 `BREAKING CHANGE` 標記描述的是開發過程中的介面變動, 不對應任何已發行的行為. 兩份記錄 `tospec metrics` 的 ADR 與其歸檔 change 維持原樣不改寫 — 它們記載的是當時所做的決定, 為配合現行名稱而重寫等同於偽造該記錄.
|
|
32
|
+
- **測試落在設計指名的兩個接縫**: 對已啟動的 server 發出 HTTP 請求, 以及以會記錄呼叫的 chart constructor 載入頁面 script. 既有的 dashboard smoke test 完全不提供 chart constructor, 那在該處可以接受, 在此處則會跳過整個產品.
|
|
33
|
+
- **新增 `TODO.md`**: 記錄一項尚未執行的 archive 流程調整 (最前面先判斷是否需要走一次 `/tosx:update`, 再往下進入 sync), 動手前需先確認 archive 的內部流程.
|
|
34
|
+
|
|
35
|
+
## [0.14.1] - 2026-07-30
|
|
36
|
+
|
|
37
|
+
打包與文件的維護版本, 無任何行為變更.
|
|
38
|
+
|
|
39
|
+
### 其他
|
|
40
|
+
|
|
41
|
+
- **`CHANGELOG.md` 納入發行內容**: 先前 `files` 未列入本檔, 使得自 npm 安裝的使用者無從得知任何一版變更了什麼 — 這正是版本升級時最需要的資訊. `package.json` 一併補上 `homepage`、`bugs` 與 `repository` 欄位, README 亦新增更新日誌連結.
|
|
42
|
+
- **`CLAUDE.md` 新增 CHANGELOG 撰寫規範**: 記載本檔的敘事體例 (每則條目需帶根因分析)、語言與標點規則、章節順序、條目結構、breaking change 標示、ADR 連結, 以及「撤銷既有條目時附註而不改寫」的歷史記錄原則. 此規範原本只存在於既有條目的行文之中, 需由撰寫者自行歸納.
|
|
43
|
+
- **新增 `docs/tospec-skills-flow.html`**: 以互動頁面呈現 13 個 Skill 的職責、串接方式與各步驟內容, 內容依 `.agents/skills/` 實際檔案整理.
|
|
44
|
+
|
|
9
45
|
## [0.14.0] - 2026-07-30
|
|
10
46
|
|
|
11
47
|
本版本主軸為修正**靜默失敗** (silent failure): 數條執行路徑雖然通過驗證、正常結束且 exit code 為 0, 實際上卻未完成應有的工作 (層級錯置的 delta spec、結構上無法觸發的 `--skip-specs`、指向不存在指令的提示訊息). 此外將使用者層級狀態統一至 `~/.config/tospec`, 使 archive 自動先執行一次 sync, 並將前一版更名為 reconcile 的工作流程還原為 sync.
|
|
@@ -221,6 +257,8 @@ Dashboard 進化為可背景常駐、多專案並存的服務, 並補上 TDD 導
|
|
|
221
257
|
|
|
222
258
|
- 新增 `prepack` script 與 npm publish 的準備設定, 完備套件發行流程.
|
|
223
259
|
|
|
260
|
+
[0.14.2]: https://github.com/seanmars/tospec/compare/v0.14.1...v0.14.2
|
|
261
|
+
[0.14.1]: https://github.com/seanmars/tospec/compare/v0.14.0...v0.14.1
|
|
224
262
|
[0.14.0]: https://github.com/seanmars/tospec/compare/v0.13.0...v0.14.0
|
|
225
263
|
[0.13.0]: https://github.com/seanmars/tospec/compare/v0.12.0...v0.13.0
|
|
226
264
|
[0.12.0]: https://github.com/seanmars/tospec/compare/v0.11.0...v0.12.0
|
package/README.md
CHANGED
|
@@ -106,6 +106,15 @@ tospec archive add-dark-mode
|
|
|
106
106
|
| `tospec status` | 依檔案與 schema 顯示各文件狀態. 選項: `--change <id>`, `--schema <name>`, `--json` |
|
|
107
107
|
| `tospec decision new <topic>` | 建立 ADR (`tospec/decisions/<yyyyMMdd_HHmmss>-<topic>.md`) 與索引列. 選項: `--title <text>`, `--summary <text>`, `--date <yyyyMMdd_HHmmss>`, `--status <proposed\|accepted\|superseded>`, `--force`, `--json` |
|
|
108
108
|
| `tospec decision list` | 以索引表列出 ADR. 選項: `--status <proposed\|accepted\|superseded>`, `--sort <date\|name>`, `--json` |
|
|
109
|
+
| `tospec skill-metrics` | 開啟本機網頁, 以圖表呈現每個 Skill 的耗時. 選項: `--no-open` |
|
|
110
|
+
|
|
111
|
+
`tospec skill-metrics` 會在 loopback 上啟動一台伺服器, 印出網址、開啟瀏覽器, 並停在前景直到 Ctrl+C. 連接埠固定從 **26693** 開始; 若已被占用則自動往上遞增直到找到可用的, 所以再開一個也不會失敗. 網頁是唯一的輸出形式, 沒有表格也沒有 `--json`; `--no-open` 只印網址而不開瀏覽器, 供沒有瀏覽器的環境使用.
|
|
112
|
+
|
|
113
|
+
頁面共五張圖: 各 Skill 的總投入時間 (實色為 engaged, 凹陷部分為 idle, 整根長度即 span)、典型的單次時長 (engaged 與 span 的中位數)、每次執行的分佈 (每一次 run 都是一個點)、每日投入時間, 以及執行時間軸. 總量與中位數刻意分成兩張: 一個 Skill 可能總量第一但每次都很短, 那是次數多而不是每次慢.
|
|
114
|
+
|
|
115
|
+
時間範圍、idle gap、split gap 都可在頁面上調整並即時重算, 選擇會記在瀏覽器中; 「只看 tospec workflow」單純隱藏列, 不重新計算. 右上角可切換主題與語言 (English / 正體中文), 兩者同樣會被記住. 終端機輸出維持英文, 與其他指令一致.
|
|
116
|
+
|
|
117
|
+
資料同時來自 Claude Code 與 Codex 兩種工具的紀錄, 一律唯讀且不寫入任何檔案. Claude Code 讀取 transcript 的 `attributionSkill` 欄位 (只存在於較新的 session, 舊紀錄無法回溯統計); Codex 的 session 沒有對應欄位, 因此改用推論 — 只要偵測到某個 Skill 的 `SKILL.md` 被讀取, 就視為該 Skill 開始執行. 由於 Codex 的歸屬是推論而非直接紀錄, 兩者精準度不同, 頁面上一律分開呈現、絕不合併成同一個數字, Codex 的數字並附上這項說明. 每一列的標籤結尾都會標上來源工具 (Claude/Codex), 兩者皆標而非只標其中一方, 以免另一方被讀成預設值. 頁面在統計為空時, 會分別指出兩種工具各自屬於哪一種情況.
|
|
109
118
|
|
|
110
119
|
### 驗證與歸檔
|
|
111
120
|
|