Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee32bfd2ec | ||
|
|
4c7b63d43a | ||
|
|
fda0fca304 | ||
|
|
18d12bbbe2 | ||
|
|
369b327a4f | ||
|
|
cce75e2f45 | ||
|
|
fbfe424c39 | ||
|
|
dd64c0350e | ||
|
|
277e6df960 | ||
|
|
aeb3f467e2 | ||
|
|
277a8f1b88 | ||
|
|
afeb0a0e7c | ||
|
|
e762cf986e | ||
|
|
7ff79005c2 | ||
|
|
c5f92ea51c | ||
|
|
2c085d68ef | ||
|
|
dc5626c6c0 | ||
|
|
e2a22b5408 | ||
|
|
aee17e45c7 | ||
|
|
089d709ca4 | ||
|
|
39bbf3c505 | ||
|
|
7f47a136e3 | ||
|
|
81e96a90d1 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-log",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.9",
|
||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||
"skills": "./skills",
|
||||
"author": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-log",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.9",
|
||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||
"skills": "./skills",
|
||||
"jsc": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# jsc-log — 工作日誌與統計
|
||||
|
||||
jsc 技能組的 log domain:工作完成後把十項資訊寫入 wiki 日誌頁(`LOG_{HASH}`,`HASH` 依共享規則計算;首碼若是數字或 `A`/`B`/`C`,改成 `H` 加上原前 7 碼;頁內仍按工作週週五整理),統計技能使用次數與呼叫鏈次數,並把技能執行的教訓記到 `LEARN_{HASH}`,供下次執行前查閱。
|
||||
jsc 技能組的 log domain:工作完成後把十項資訊寫入 wiki 日誌頁(`LOG_{HASH}`,`HASH` 依共享規則計算,取 `{owner}/{repo}` 的完整 SHA-1 四十碼、a-f 轉大寫,不截短也不加前綴;頁內仍按工作週週五整理),統計技能使用次數與呼叫鏈次數,並把技能執行的教訓記到 `LEARN_{HASH}`,供下次執行前查閱。目錄頁(`LOG_CONTENTS`、`LEARN_CONTENTS`、`REPORT_CONTENTS`)另住一個專用存取庫,與內容頁分開。目錄頁的版面是「H1 加 `>` 引言,再一筆一個 H2 區塊」:H2 標題就是那一筆的內容頁頁名,也就是鍵,欄位寫成標題底下的 `- {欄位名}:{值}` 條列,頁上不留 markdown 表格。
|
||||
|
||||
## 安裝、更新、移除
|
||||
|
||||
@@ -24,7 +24,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
| --- | --- |
|
||||
| `tools/usage-stats.sh` | 聚合 `$JSC_HOME/usage/*.jsonl`:`skills` 列技能使用次數、`chains` 列呼叫鏈次數(皆降冪),`--cli <name>` 過濾 |
|
||||
| `tools/worklog-target.sh` | 接收已由 `jsc-gitea/tools/hash-id` 算好的 `HASH`,組出 `LOG_{HASH}`、`LOG_CONTENTS`(本身不再計算 SHA-1)。`friday [yyyy-MM-dd]` 印出該工作週的週五,週界向 `report-range.sh weekly` 取得,這裡只加四天——跨月、跨年那一週交給人算就會差一天 |
|
||||
| `tools/worklog-pending.sh` | 待寫入日誌的暫存區,存放於 `$JSC_HOME/worklog-pending/{HASH}/`。`add {HASH} {檔案}` 存一段內容(`jsc-sdlc` 的階段回報發現沒寫日誌時會呼叫),`cat {HASH}` 依時間印出全部、`list` 列路徑、`clear` 清掉全部。寫日誌走三段式:`merge {HASH} {本次條目檔}` 合成「暫存內容在前、本次條目在後」並印出 `MERGED=`、`CLAIM=`、`PENDING=`;wiki 寫入成功後 `commit {HASH} {CLAIM}` 只清掉併入清單上那幾個檔;寫入失敗就 `abort {HASH} {CLAIM}`,暫存一個都不刪。結束碼 `3` 代表沒有暫存內容(`merge` 沒暫存仍是 `0`)。**清除只發生在寫進 wiki 成功之後**,先清再寫會兩邊都沒有 |
|
||||
| `tools/worklog-pending.sh` | 待寫入日誌的暫存區,存放於 `$JSC_HOME/worklog-pending/{HASH}/`。`{HASH}` 只收三種:40 碼大寫十六進位(現行),或 8 碼大寫十六進位、`H` 加 7 碼大寫十六進位(尚未遷移的舊暫存;舊規則把首碼落在 `0-9ABC` 的 hash 改寫成 `H` 加原前 7 碼,所以舊暫存大多長這樣),其餘一律 `2`——這道格式檢查擋的是路徑穿越。`add {HASH} {檔案}` 存一段內容(`jsc-sdlc` 的階段回報發現沒寫日誌時會呼叫),`cat {HASH}` 依時間印出全部、`list` 列路徑、`clear` 清掉全部。寫日誌走三段式:`merge {HASH} {本次條目檔}` 合成「暫存內容在前、本次條目在後」並印出 `MERGED=`、`CLAIM=`、`PENDING=`;wiki 寫入成功後 `commit {HASH} {CLAIM}` 只清掉併入清單上那幾個檔;寫入失敗就 `abort {HASH} {CLAIM}`,暫存一個都不刪。結束碼 `3` 代表沒有暫存內容(`merge` 沒暫存仍是 `0`)。**清除只發生在寫進 wiki 成功之後**,先清再寫會兩邊都沒有 |
|
||||
| `tools/report-range.sh` | 算報表期間:`report-range.sh {daily\|weekly\|monthly\|yearly} [yyyy-MM-dd]` 印出「起<TAB>訖<TAB>標籤<TAB>期間」,含頭含尾。週採 ISO-8601(週一起算),標籤如 `2026-W35`。日期運算交給系統的 `date`,不自己算閏年 |
|
||||
| `tools/report-template.sh` | 解析報表範本位置:`resolve {period}` 印出「路徑<TAB>project\|skill」,`list` 一次列四種期間。工作目錄的 `.jsc/templates/report-{period}.md` 優先於技能自帶的 `templates/` |
|
||||
| `tools/log-aggregate.sh` | 把日誌頁彙總成報表數字:`log-aggregate.sh {起} {訖} [日誌頁檔案 ...]`(省略檔案就讀標準輸入),印出 `ENTRIES=`、`REPOS=`、`REPO=`、`ELAPSED_MINUTES=`、`ELAPSED_ENTRIES=`、`ELAPSED_MISSING=`、`TOKEN=`、`TOKEN_MISSING=`、`STATUS=`。「無資料不估算」寫在腳本裡:沒填花費時間的條目不進總和,只進 `ELAPSED_MISSING`;整段期間都沒有時間就印「無資料」,不印 `0`。結束碼 `3` 代表期間內沒有條目,全零結果照樣印出來 |
|
||||
@@ -40,7 +40,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
|
||||
每完成一個任務就寫一筆日誌。任務有三種:一個工作包、一輪 PR 留言修正、一個獨立的修正提交。下一個任務開始前先把這一筆寫完,同一個工作包跑五輪留言修正就是五筆,各自帶自己的花費時間與 token 用量,附加到同一頁 `LOG_{HASH}`——連「試了卻沒改到檔案」的那一輪也留下來,那段時間才看得見。
|
||||
|
||||
每筆蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支)。計畫連結、工作包連結、花費時間、token 用量四項來源互不相依,併行取得;存取庫解析與 `HASH` 計算也併行。用 `tools/worklog-target.sh` 產生目標頁,工作週的週五由同一支的 `friday` 子命令算出,套範本後附加到 `LOG_{HASH}` 與 `LOG_CONTENTS`。寫入前跑 `tools/worklog-pending.sh merge {HASH} {本次條目檔}`:之前有階段跑完沒寫日誌,內容暫存在那裡,這次一併寫進去;wiki 寫入成功才 `commit` 清掉暫存,失敗就 `abort` 保留。
|
||||
每筆蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支)。計畫連結、工作包連結、花費時間、token 用量四項來源互不相依,併行取得;存取庫解析與 `HASH` 計算也併行。用 `tools/worklog-target.sh` 產生目標頁,工作週的週五由同一支的 `friday` 子命令算出,套範本後附加到 `LOG_{HASH}`。目錄頁 `LOG_CONTENTS` 住在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,與 `LOG_{HASH}` 不同 wiki,改由 `jsc-gitea/tools/wiki-contents.sh upsert LOG 1 LOG_{HASH}` 單一區塊寫回:鍵是 H2 標題的頁名 `LOG_{HASH}`,「日誌頁」那一條的絕對網址(`gitea.sh wiki-url` 給的)只給人點。網址帶主機名,拿它當鍵換主機就比不到,同一頁會多一個區塊;頁名只由 `{owner}/{repo}` 決定,不受影響。命令裡的 `1` 是 `<key-col>`,只在舊頁還是表格時用來認出哪一欄的文字當 H2 標題。寫入前跑 `tools/worklog-pending.sh merge {HASH} {本次條目檔}`:之前有階段跑完沒寫日誌,內容暫存在那裡,這次一併寫進去;wiki 寫入成功才 `commit` 清掉暫存,失敗就 `abort` 保留。
|
||||
|
||||
### `stats`
|
||||
|
||||
@@ -48,11 +48,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
|
||||
### `learn`
|
||||
|
||||
技能執行後把教訓(日期、技能、CLI、情境、教訓、下次做法)附加到 `LEARN_{HASH}` 並更新 `LEARN_CONTENTS`;技能執行前查閱同兩頁,套用相符的「下次做法」。
|
||||
技能執行後把教訓(日期、技能、CLI、情境、教訓、下次做法)附加到 `LEARN_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 LEARN_{HASH}` 更新 `LEARN_CONTENTS` 上該存取庫那個區塊;技能執行前查閱同兩頁,套用相符的「下次做法」。兩頁分屬不同存取庫:`LEARN_{HASH}` 取 `JSC_WIKI_REPO_LEARN`,`LEARN_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS`,區塊裡的連結用絕對網址。
|
||||
|
||||
### `report`
|
||||
|
||||
把工作日誌總結成年報、月報、週報或日報。期間由 `tools/report-range.sh` 算出(週次採 ISO-8601),範本由 `tools/report-template.sh` 解析——工作目錄的 `.jsc/templates/report-{period}.md` 優先,沒有才用技能自帶的那份。範本解析、日誌頁讀取、教訓頁讀取三線併行,各頁也一頁一個 sub agent 同時讀。讀 `LOG_CONTENTS` 列出的所有日誌頁後,交給 `tools/log-aggregate.sh` 算出條目數、涵蓋存取庫、花費時間、各 CLI token 用量與狀態計數,填進範本後寫入 wiki `REPORT_{HASH}`(`HASH` 取 `{owner}/{repo}/{期間}`,這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫——本頁其他 `HASH` 取的是程式碼存取庫,只有這一處不同),同一期間重跑只換掉那一節。年報的教訓頁另解 `JSC_WIKI_REPO_LEARN`,不沿用日誌頁的存取庫。單筆工作紀錄請用 `worklog`。
|
||||
把工作日誌總結成年報、月報、週報或日報。期間由 `tools/report-range.sh` 算出(週次採 ISO-8601),範本由 `tools/report-template.sh` 解析——工作目錄的 `.jsc/templates/report-{period}.md` 優先,沒有才用技能自帶的那份。範本解析、日誌頁讀取、教訓頁讀取三線併行,各頁也一頁一個 sub agent 同時讀。目錄頁 `LOG_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS` 的專用存取庫,它列出的日誌頁用絕對網址逐頁讀回;讀完交給 `tools/log-aggregate.sh` 算出條目數、涵蓋存取庫、花費時間、各 CLI token 用量與狀態計數,填進範本後寫入 wiki `REPORT_{HASH}`(`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼,這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫——本頁其他 `HASH` 取的是程式碼存取庫,只有這一處不同),同一期間重跑只換掉那一節。`REPORT_CONTENTS` 那個區塊改由 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 REPORT_{HASH}` 寫回,鍵同樣是 H2 標題的頁名。年報的教訓「內容頁」另解 `JSC_WIKI_REPO_LEARN`,不沿用日誌頁的存取庫;目錄頁同住 CONTENTS 存取庫是另一回事,兩者別混。單筆工作紀錄請用 `worklog`。
|
||||
|
||||
<!-- JSC-SKILLS:END -->
|
||||
|
||||
@@ -74,7 +74,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
|
||||
## 環境變數
|
||||
|
||||
Wiki 位置:日誌頁用 `JSC_WIKI_REPO_LOG`、教訓頁用 `JSC_WIKI_REPO_LEARN`、報表頁用 `JSC_WIKI_REPO_REPORT`,未設定退回 `JSC_WIKI_REPO`,再未設定就詢問(見 `jsc-gitea`)。先讀目前 shell 繼承的環境變數,缺值才詢問。
|
||||
Wiki 位置分兩層:**內容頁**一頁型一個變數——日誌頁 `LOG_{HASH}` 用 `JSC_WIKI_REPO_LOG`、教訓頁 `LEARN_{HASH}` 用 `JSC_WIKI_REPO_LEARN`、報表頁 `REPORT_{HASH}` 用 `JSC_WIKI_REPO_REPORT`;**目錄頁**三頁共用一個變數——`LOG_CONTENTS`、`LEARN_CONTENTS`、`REPORT_CONTENTS` 一律用 `JSC_WIKI_REPO_CONTENTS`。
|
||||
|
||||
以上每個變數未設定都退回 `JSC_WIKI_REPO`,再未設定就詢問(見 `jsc-gitea`)。先讀目前 shell 繼承的環境變數,缺值才詢問。
|
||||
|
||||
目錄頁與內容頁從此分屬不同存取庫,兩件事跟著改:`JSC_WIKI_REPO_CONTENTS` 不會退回頁型變數,`JSC_WIKI_REPO_LOG` 之類也頂不了目錄頁的位;連結一律寫成 `[{文字}]({連結})`,網址取 `gitea.sh wiki-url` 印出的絕對網址,不自己組路徑。同 wiki 的雙括號寫法一概不用:它只在自己那個 wiki 內解得開,跨庫就是死連結,畫面上還看不出壞掉。連結寫進頁面前先過 `gitea.sh` 旁邊的 `link-check.sh`,結束碼 0 才寫。
|
||||
|
||||
## 相關 domain
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-log",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.9",
|
||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||
"skills": "./skills/",
|
||||
"jsc": {
|
||||
|
||||
+15
-15
@@ -7,37 +7,37 @@
|
||||
| 項目 | 內容 |
|
||||
| --- | --- |
|
||||
| 觸發時機 | 一次技能執行留下可重用的教訓時,用 record 模式記錄。要跑某支技能之前,用 consult 模式查過去的教訓。工時與 Token 紀錄不走這支,走 worklog |
|
||||
| 關鍵步驟 | record 模式收齊日期、技能、CLI、情境、教訓、下次做法這六欄、從 `git remote get-url origin` 解出 `{owner}/{repo}`、用 `hash-id` 算出 `{HASH}`、開 sub agent 讀 `LEARN_{HASH}`、依 `wiki-get` 的退出碼分支(0 在表尾追加一列、4 才用 `templates/learn-page.md` 建頁、7 與 8 停止並回報)、同一輪更新 `LEARN_CONTENTS`、寫入失敗重試一次;consult 模式算出 `{HASH}`、讀 `LEARN_CONTENTS` 與 `LEARN_{HASH}`、挑出「技能」欄相符的列、整理每一列的「下次做法」交給呼叫端 |
|
||||
| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh wiki-repo LEARN`、`jsc-gitea:wiki`(全部 wiki 讀寫)、`git remote get-url origin`、`templates/learn-page.md`、`templates/learn-contents.md` |
|
||||
| 完成條件 | record 模式要 sub agent 回報兩頁都寫入成功,主代理確認新列在 `LEARN_{HASH}` 上,原有的列一列不少。consult 模式要兩頁都讀到,或以退出碼 4 回報頁面不存在,或在 5、7、8 停止並回報狀態 |
|
||||
| 可驗證跡象 | wiki 的 `LEARN_{HASH}` 表尾多一列教訓,`LEARN_CONTENTS` 上該 repo 那一列的「最後更新時間」換新。頁面原本不存在時,會新建 `LEARN_{HASH}` 或 `LEARN_CONTENTS`。consult 模式無寫入跡象,只有回報內容 |
|
||||
| 關鍵步驟 | record 模式收齊日期、技能、CLI、情境、教訓、下次做法這六欄、從 `git remote get-url origin` 解出 `{owner}/{repo}`、用 `hash-id` 算出完整 40 碼大寫的 `{HASH}`、用 `wiki-repo LEARN` 解出教訓頁存取庫、開 sub agent 讀 `LEARN_{HASH}`、依 `wiki-get` 的退出碼分支(0 在表尾追加一列、4 才用 `templates/learn-page.md` 建頁、7 與 8 停止並回報)、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、把該網址交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、照 `templates/learn-contents.md` 組出一個 H2 區塊檔(`## LEARN_{HASH}` 加空行加各條 `- {欄位名}:{值}`)、同一輪跑 `wiki-contents.sh upsert LEARN 2 LEARN_{HASH}` 更新落在 CONTENTS 存取庫的 `LEARN_CONTENTS`(鍵是 H2 標題的頁名,不是那條帶主機名的連結;命令裡的 `2` 是 `<key-col>`,只在舊頁還是表格時用來認出哪一欄的文字當標題)、依它的退出碼分流(0 已寫、1 組不出內容或寫入失敗、2 參數錯、3 未設 `JSC_WIKI_REPO_CONTENTS`、4 缺範本、7 金鑰失效、8 其他 API 失敗)、寫入失敗重試一次;consult 模式算出 `{HASH}`、從 CONTENTS 存取庫讀 `LEARN_CONTENTS`、從 LEARN 存取庫讀 `LEARN_{HASH}`、依區塊裡「教訓紀錄」那一條的絕對網址讀頁、挑出「技能」欄相符的列、整理每一列的「下次做法」交給呼叫端;兩種模式都以 `jsc-hooks/tools/report-status.sh skill-end jsc-log:learn {status} {結束碼} {detail}` 收尾,腳本不在這台機器上就安靜跳過 |
|
||||
| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh wiki-repo LEARN` 與 `wiki-repo CONTENTS` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea:wiki`(其餘 wiki 讀寫)、`git remote get-url origin`、`templates/learn-page.md`、`templates/learn-contents.md` |
|
||||
| 完成條件 | record 模式要 sub agent 回報兩頁都寫入成功,主代理確認新列在 `LEARN_{HASH}` 上、該存取庫那個區塊在 `LEARN_CONTENTS` 上,原有的列與區塊一個不少,寫入前 `link-check.sh` 回 0,`wiki-contents.sh` 回 0 並印出 `updated` 或 `added`。`link-check.sh` 回 1 就不寫目錄頁,改回報 DEAD 清單。consult 模式要兩頁都讀到,或以退出碼 4 回報頁面不存在,或在 5、7、8 停止並回報狀態。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都成功是 `ok`,內容頁寫成功但目錄頁沒更新是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,雜湊工具缺席或 wiki 存取庫沒設定是 `blocked`,使用者中止或根本沒有可記的教訓是 `aborted` |
|
||||
| 可驗證跡象 | LEARN 存取庫的 `LEARN_{HASH}` 表尾多一列教訓;CONTENTS 存取庫的 `LEARN_CONTENTS` 上標題為 `LEARN_{HASH}` 的那個區塊,「最後更新時間」那一條換新,「教訓紀錄」那一條是 `[{頁名}]({絕對網址})`,頁面上沒有 markdown 表格,也沒有 `[[...]]` 這種同 wiki 寫法。頁面原本不存在時,會新建 `LEARN_{HASH}` 或 `LEARN_CONTENTS`;`LEARN_CONTENTS` 建出來只有 H1 與 `>` 引言,範本的示範區塊不會留在上面。舊的表格式目錄頁會在同一輪整頁轉成區塊,別的存取庫那幾筆原樣轉過去。連結驗證不過就兩頁都沒有新內容,只有 DEAD 清單的回報。consult 模式在 wiki 上無寫入跡象,只有回報內容。兩種模式跑完,`$JSC_HOME/usage/events.jsonl` 都會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:learn`,`status` 欄是那五個值之一;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 |
|
||||
|
||||
## report
|
||||
|
||||
| 項目 | 內容 |
|
||||
| --- | --- |
|
||||
| 觸發時機 | 有人要一段期間的工作總結時用,期間分年、月、週、日四種。單一工作包的紀錄不走這支,走 worklog |
|
||||
| 關鍵步驟 | 呼叫端沒指定期間就依 `jsc-ask:ask` 問、跑 `tools/report-range.sh` 取得起訖日與標籤、平行做四件事(`tools/report-template.sh resolve` 解出範本、讀 `LOG_CONTENTS` 與它列出的每一頁工作日誌、年報另外讀 `LEARN_CONTENTS`、解出 REPORT 的 wiki repo)、把頁面內容存成檔案後跑 `tools/log-aggregate.sh` 算出條目數、涵蓋 repo、花費時間、Token 用量、任務狀態、從存活條目挑出阻塞與未完成工作包、照範本的標題與表格填出報告、讀 `REPORT_{HASH}` 後把本期當成新章節追加在最前、更新 `REPORT_CONTENTS` |
|
||||
| 外部呼叫 | `tools/report-range.sh`、`tools/report-template.sh`、`tools/log-aggregate.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(LOG、LEARN、REPORT)與 `hash-id`、`jsc-gitea:wiki`、`jsc-ask:ask`、`templates/report-{period}.md`、`templates/report-contents.md` |
|
||||
| 完成條件 | 回報頁面 URL,或回報跳過寫入與它的原因,或在讀取回 7、8 時停下並回報狀態。收尾要講出期間標籤、條目數、涵蓋的 repo、範本來源、頁面 URL、帶到下一期的未完成工作包清單,以及 `ELAPSED_MISSING` 與 `TOKEN_MISSING` |
|
||||
| 可驗證跡象 | wiki 的 `REPORT_{HASH}`(雜湊取自 `{owner}/{repo}/{period}`)多一個本期章節,`REPORT_CONTENTS` 該列的「最新一期」、「期數」、「最後更新」換新。本機留下工作日誌頁面內容的暫存檔,供 `log-aggregate.sh` 讀取。沒設定 REPORT wiki repo 時不寫 wiki,只印出報告本文 |
|
||||
| 關鍵步驟 | 呼叫端沒指定期間就依 `jsc-ask:ask` 問、跑 `tools/report-range.sh` 取得起訖日與標籤、平行做四件事(`tools/report-template.sh resolve` 解出範本、從 CONTENTS 存取庫讀 `LOG_CONTENTS` 並照每個區塊裡「日誌頁」那一條的絕對網址逐頁讀回工作日誌、年報另外讀 `LEARN_CONTENTS` 並用 `wiki-repo LEARN` 解出教訓內容頁的存取庫、解出 REPORT 的 wiki repo)、把頁面內容存成檔案後跑 `tools/log-aggregate.sh` 算出條目數、涵蓋 repo、花費時間、Token 用量、任務狀態、從存活條目挑出阻塞與未完成工作包、照範本的標題與表格填出報告、用 `hash-id "{REPORT wiki 存取庫}/{period}"` 算出完整 40 碼頁名、讀 `REPORT_{HASH}` 後把本期當成新章節追加在最前、取 `wiki-url` 的絕對網址並依它的退出碼分流(4 是頁還沒寫、5 是頁上沒有 `html_url`、7 與 8 一律停下並回報金鑰或 API 狀態,不得當成 4)、章節內與目錄區塊的連結一律寫成 `[{文字}]({絕對網址})` 並在寫入前全數交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、照 `templates/report-contents.md` 組出一個 H2 區塊檔(`## REPORT_{HASH}` 加空行加各條 `- {欄位名}:{值}`)、跑 `wiki-contents.sh upsert REPORT 1 REPORT_{HASH}` 更新落在 CONTENTS 存取庫的 `REPORT_CONTENTS`(鍵是 H2 標題的頁名,不是「報表頁」那條帶主機名的連結;命令裡的 `1` 是 `<key-col>`,只在舊頁還是表格時用來認出哪一欄的文字當標題)並依它的退出碼分流(0 已寫、1 組不出內容或寫入失敗、2 參數錯、3 未設 `JSC_WIKI_REPO_CONTENTS`、4 缺範本、7 金鑰失效、8 其他 API 失敗)、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:report {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 |
|
||||
| 外部呼叫 | `tools/report-range.sh`、`tools/report-template.sh`、`tools/log-aggregate.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(CONTENTS、LOG、LEARN、REPORT)與 `hash-id` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea:wiki`、`jsc-ask:ask`、`templates/report-{period}.md`、`templates/report-contents.md` |
|
||||
| 完成條件 | 回報頁面 URL,或回報跳過寫入與它的原因,或在讀取回 7、8 時停下並回報狀態。每次寫入前 `link-check.sh` 要回 0,回 1 就不寫該頁並回報 DEAD 清單。`wiki-contents.sh upsert` 要回 0 並印出 `updated` 或 `added`,回 1、2、3、4、7、8 就照該碼回報且不得謊報已寫入。收尾要講出期間標籤、條目數、涵蓋的 repo、範本來源、頁面 URL、帶到下一期的未完成工作包清單,以及 `ELAPSED_MISSING` 與 `TOKEN_MISSING`。收尾一定要寫一筆 `skill-end` 狀態事件:章節與目錄區塊都到位是 `ok`(範圍內沒有條目、`log-aggregate.sh` 回 3 也算 `ok`,`detail` 帶 `ENTRIES=0`),報告本文交出去但 wiki 沒寫全是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,期間算不出來或範本與存取庫沒設定是 `blocked`,使用者中止是 `aborted` |
|
||||
| 可驗證跡象 | REPORT 存取庫的 `REPORT_{HASH}`(雜湊取自 REPORT wiki 存取庫的 `{owner}/{repo}` 加期間,不是程式碼存取庫)多一個本期章節;CONTENTS 存取庫的 `REPORT_CONTENTS` 上標題為 `REPORT_{HASH}` 的那個區塊,「最新一期」、「期數」、「最後更新」三條換新,「報表頁」那一條是 `[{頁名}]({絕對網址})`、「HASH」那一條是不帶連結的裸 HASH,目錄頁上沒有 markdown 表格,兩頁也都找不到 `[[...]]` 這種同 wiki 寫法。連結驗證不過就沒有新章節,也沒有新的目錄區塊,只有 DEAD 清單的回報。重跑同一期間只換掉那個區塊,不會多出第二個;四種期間各自一個區塊,因為 `HASH` 帶著期間。舊的表格式目錄頁會在同一輪整頁轉成區塊,別人那幾筆原樣轉過去。本機留下工作日誌頁面內容的暫存檔,供 `log-aggregate.sh` 讀取。沒設定 REPORT wiki repo 時不寫 wiki,只印出報告本文;沒設定 `JSC_WIKI_REPO_CONTENTS` 時內容頁照寫,只有目錄頁那個區塊沒動。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:report`,`status` 與 `exit` 兩欄對得上上一列講的判準;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 |
|
||||
|
||||
## stats
|
||||
|
||||
| 項目 | 內容 |
|
||||
| --- | --- |
|
||||
| 觸發時機 | 使用者問某支技能用了幾次,或問技能之間的呼叫鏈用了幾次。Token 與工時統計不走這支,走 worklog |
|
||||
| 關鍵步驟 | 直接跑 `tools/usage-stats.sh skills` 或 `tools/usage-stats.sh chains`、需要時加 `--cli` 篩選單一 CLI、先讀退出碼再看輸出、退出碼 0 就把每一行印成表格一列、一行都沒印也是退出碼 0,代表次數為零,要說明 `jsc-hooks` 還沒經 `jsc-hooks:hooks-install` 安裝接線且不出表、退出碼 2 代表子指令或 `--cli` 參數被拒,改正參數後重跑 |
|
||||
| 關鍵步驟 | 直接跑 `tools/usage-stats.sh skills` 或 `tools/usage-stats.sh chains`、需要時加 `--cli` 篩選單一 CLI、先讀退出碼再看輸出、退出碼 0 就把每一行印成表格一列、一行都沒印也是退出碼 0,代表次數為零,要說明 `jsc-hooks` 還沒經 `jsc-hooks:hooks-install` 安裝接線且不出表、退出碼 2 代表子指令或 `--cli` 參數被拒,改正參數後重跑、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:stats {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 |
|
||||
| 外部呼叫 | `tools/usage-stats.sh`;資料由 `jsc-hooks/hooks/skill-usage.sh` 持續寫進 `$JSC_HOME/usage/*.jsonl`,這支技能只讀不寫 |
|
||||
| 完成條件 | 讀到退出碼,並走完該退出碼指定的分支 |
|
||||
| 可驗證跡象 | 無寫入跡象,只有回報內容 |
|
||||
| 完成條件 | 讀到退出碼,並走完該退出碼指定的分支。收尾一定要寫一筆 `skill-end` 狀態事件:退出碼 0 是 `ok`(一行都沒印也是 `ok`,`detail` 帶 `count=0`),只答出一半的統計是 `degraded`,退出碼 2 是 `failed`(那一輪什麼都沒讀到,不得當成次數為零),統計腳本不在這台機器上是 `blocked`,使用者中止或問的其實是工時與 Token 是 `aborted` |
|
||||
| 可驗證跡象 | 統計本身無寫入跡象,只有回報內容。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:stats`;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 |
|
||||
|
||||
## worklog
|
||||
|
||||
| 項目 | 內容 |
|
||||
| --- | --- |
|
||||
| 觸發時機 | implement 或 maintain 階段每結束一項任務就寫一筆。一項任務是一個工作包、一輪 PR 意見修正,或一次獨立的修正提交。下一項任務開始前就要寫完。規劃階段的筆記不走這支 |
|
||||
| 關鍵步驟 | 全程由 sub agent 收集與寫入、平行取得十項事實(`git remote get-url origin` 的 repo、`git branch --show-current` 的分支、PLAN 頁絕對連結、ANALYZE 頁工作包絕對連結、`session-timer.sh report` 的花費時間、`token-usage.sh` 的各 CLI Token、任務狀態、細節與產出、困難與解法、PR 目標分支)、平行解出 LOG wiki repo 與 `{HASH}`、跑 `tools/worklog-target.sh` 取得頁名與本週五日期、用 `templates/log-entry.md` 填出單筆條目檔、跑 `tools/worklog-pending.sh merge` 併入待寫內容、讀 `PAGE` 後依退出碼分支(0 追加在頁尾、4 才建頁、7 與 8 停止且不建頁)、同一輪更新 `CONTENTS`、最後依成敗跑 `worklog-pending.sh commit` 或 `abort` |
|
||||
| 外部呼叫 | `jsc-hooks/hooks/session-timer.sh report`、`tools/token-usage.sh`、`tools/worklog-target.sh`、`tools/worklog-pending.sh`、`jsc-gitea/tools/gitea.sh wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`、`jsc-ask:ask`、`git remote get-url origin`、`git branch --show-current`、`templates/log-entry.md`、`templates/log-contents.md` |
|
||||
| 完成條件 | `MERGED` 的每一筆條目都在 `PAGE` 上,原有條目逐字不動,`CONTENTS` 該列帶著本週五日期且其他列不動,`worklog-pending.sh` 的 `commit` 或 `abort` 其中一個跑過並回報退出碼 |
|
||||
| 可驗證跡象 | wiki 的 `LOG_{HASH}` 頁尾多一筆條目,`LOG_CONTENTS` 該列的「條目數」與「最後更新」換新。`$JSC_HOME/worklog-pending/{HASH}` 底下的待寫檔在 `commit` 後清空,`abort` 後原樣保留。本機留下填好的條目檔 |
|
||||
| 關鍵步驟 | 全程由 sub agent 收集與寫入、平行取得十項事實(`git remote get-url origin` 的 repo、`git branch --show-current` 的分支、PLAN 頁絕對連結、ANALYZE 頁工作包絕對連結、`session-timer.sh report` 的花費時間、`token-usage.sh` 的各 CLI Token、任務狀態、細節與產出、困難與解法、PR 目標分支)、平行解出 LOG wiki repo(只供內容頁)與完整 40 碼大寫的 `{HASH}`、跑 `tools/worklog-target.sh` 取得頁名與本週五日期、用 `templates/log-entry.md` 填出單筆條目檔、跑 `tools/worklog-pending.sh merge` 併入待寫內容(同時收編同一個 `{HASH}` 的「`H` 加前 7 碼」舊目錄)、跑 `tools/worklog-pending.sh orphans` 掃出現行規則定址不到的暫存目錄(0 就安靜帶過,4 就把每一列回報給使用者並繼續本輪)、讀 `PAGE` 後依退出碼分支(0 追加在頁尾、4 才建頁、7 與 8 停止且不建頁)、寫入前把 `MERGED` 裡的每個連結交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、同一輪用 `wiki-repo CONTENTS` 解出目錄頁存取庫、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、同樣先過 `link-check.sh`、照 `templates/log-contents.md` 組出一個 H2 區塊檔(`## LOG_{HASH}` 加空行加各條 `- {欄位名}:{值}`)才跑 `wiki-contents.sh upsert LOG 1 LOG_{HASH}` 更新 `LOG_CONTENTS`(鍵是 H2 標題的頁名,不是「日誌頁」那條帶主機名的連結;命令裡的 `1` 是 `<key-col>`,只在舊頁還是表格時用來認出哪一欄的文字當標題)並依 0、1、2、3、4、7、8 各自分流(1 是組不出內容或寫入失敗,找不到區塊只是走附加,不算錯)、最後依成敗跑 `worklog-pending.sh commit` 或 `abort`、再跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:worklog {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 |
|
||||
| 外部呼叫 | `jsc-hooks/hooks/session-timer.sh report`、`tools/token-usage.sh`、`tools/worklog-target.sh`、`tools/worklog-pending.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(LOG 與 CONTENTS)與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`、`jsc-ask:ask`、`git remote get-url origin`、`git branch --show-current`、`templates/log-entry.md`、`templates/log-contents.md` |
|
||||
| 完成條件 | 兩次寫入前 `link-check.sh` 都回 0,`MERGED` 的每一筆條目都在 `PAGE` 上,原有條目逐字不動,`wiki-contents.sh upsert` 回 0 且 `CONTENTS` 上標題為 `LOG_{HASH}` 的那個區塊帶著本週五日期、其他區塊不動,`worklog-pending.sh` 的 `commit` 或 `abort` 其中一個跑過並回報退出碼,`orphans` 也跑過且回 0 或已把孤兒清單回報出去。`link-check.sh` 回 1 或 `upsert` 回非 0 就照該碼回報,並把步驟七當成失敗處理,讓待寫內容留著。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都到位是 `ok`(`orphans` 掃到孤兒仍算 `ok`,但 `detail` 要帶筆數,因為孤兒屬於別的存取庫,不影響本輪結論),內容頁寫成功而 `LOG_CONTENTS` 沒更新是 `degraded`,連結驗證回 1 不寫是 `failed`,雜湊工具或日期運算缺席、wiki 存取庫沒設定是 `blocked`,使用者中止或這一輪根本沒有任務結束是 `aborted` |
|
||||
| 可驗證跡象 | LOG 存取庫的 `LOG_{HASH}` 頁尾多一筆條目,條目裡的計畫名稱、工作包編號、PR 目標分支三欄都是 `[{文字}]({絕對網址})`;CONTENTS 存取庫的 `LOG_CONTENTS` 上標題為 `LOG_{HASH}` 的那個區塊,「條目數」與「最後更新」兩條換新,「日誌頁」那一條是 `[{頁名}]({絕對網址})`、「HASH」那一條是不帶連結的裸 HASH,目錄頁上沒有 markdown 表格,也沒有 `[[...]]` 這種同 wiki 寫法。連結驗證不過就兩頁都沒有新內容,待寫檔原樣保留。重跑同一頁只換掉那個區塊,不會多出第二個。舊的表格式目錄頁會在同一輪整頁轉成區塊,別的日誌頁那幾筆原樣轉過去。`$JSC_HOME/worklog-pending/{HASH}` 底下的待寫檔在 `commit` 後清空,`abort` 後原樣保留;該目錄名是 40 碼大寫十六進位,或尚未遷移的舊暫存那種 8 碼大寫十六進位、`H` 加 7 碼大寫十六進位;推得出對映的舊目錄會連同新目錄一起被清掉。暫存區留下非 40 碼的目錄時,該輪的回報上看得到 `orphans` 印出的那幾列。本機留下填好的條目檔。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:worklog`,`status` 與 `exit` 兩欄對得上上一列講的判準,孤兒筆數落在 `detail` 欄;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 |
|
||||
|
||||
+56
-8
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: learn
|
||||
description: Record a lesson learned after a skill run to wiki LEARN_{HASH} plus LEARN_CONTENTS, or consult past lessons before a skill run. Each entry is one table row with date, skill, CLI, situation, lesson, and next-time approach. HASH follows the shared 8-char rule with the H-prefix fallback. Use when a skill run produced a reusable lesson, or before running a skill to consult past lessons; not for work-time logs (see worklog).
|
||||
description: Record a lesson learned after a skill run to wiki LEARN_{HASH} plus LEARN_CONTENTS, or consult past lessons before a skill run. Each entry is one table row with date, skill, CLI, situation, lesson, and next-time approach. HASH is the full 40-character uppercase SHA-1 of {owner}/{repo}; LEARN_{HASH} sits in the LEARN wiki repo while LEARN_CONTENTS sits in the separate CONTENTS repo, so the directory entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name LEARN_{HASH}, with a bullet per field and the lesson page linked by its absolute wiki-url. Use when a skill run produced a reusable lesson, or before running a skill to consult past lessons; not for work-time logs (see worklog).
|
||||
---
|
||||
|
||||
# learn — lessons learned
|
||||
@@ -10,9 +10,12 @@ Close the loop on skill runs: record what a run taught you, consult it before th
|
||||
## Target pages
|
||||
|
||||
- Directory page: `LEARN_CONTENTS`. Content page: `LEARN_{HASH}`, one page per repository.
|
||||
- Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. Exit 1 means no SHA-1 helper on this machine: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash.
|
||||
- Wiki repo: run `jsc-gitea/tools/gitea.sh wiki-repo LEARN`. Exit 3 hands the question to `jsc-gitea:wiki`, which owns the resolution order and the wording; exit 2 means the type argument was misspelled, so fix it and rerun.
|
||||
- All wiki reads and writes go through `jsc-gitea:wiki`.
|
||||
- The two pages live in different wikis. `LEARN_{HASH}` goes to `jsc-gitea/tools/gitea.sh wiki-repo LEARN`; `LEARN_CONTENTS` goes to `gitea.sh wiki-repo CONTENTS` (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3), which never falls back to the LEARN repo. Exit 3 on either hands the question to `jsc-gitea:wiki`, which owns the resolution order and the wording; exit 2 means the type argument was misspelled, so fix it and rerun.
|
||||
- Every link this skill writes takes the shape `[{text}]({absolute URL})`. The directory block's 教訓紀錄 bullet links the lesson page as `[LEARN_{HASH}](<url>)`, with `<url>` from `gitea.sh wiki-url <LEARN repo> LEARN_{HASH}` — never assembled by hand, and never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from the directory without reporting an error. `wiki-url` exit 4 means the lesson page is not written yet, so write it first; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status.
|
||||
- Check every link before it reaches a page: `jsc-gitea/tools/link-check.sh {url}...`. Only exit 0 permits the write.
|
||||
- Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. It prints the full 40-character uppercase SHA-1 — no truncation and no prefix rewrite, so never shorten it. Exit 1 means no SHA-1 helper on this machine: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash. Exit 2 means the input was empty, so fix the `{owner}/{repo}` parse and rerun.
|
||||
- All wiki reads and writes go through `jsc-gitea:wiki`, except the `LEARN_CONTENTS` block, which goes through `jsc-gitea/tools/wiki-contents.sh`.
|
||||
- The two pages carry different shapes. `LEARN_{HASH}` stays a markdown table, one row per lesson. `LEARN_CONTENTS` is a list page: `# 教訓目錄`, a `>` preamble, then one H2 block per repository whose heading is that repository's lesson page name.
|
||||
|
||||
## Mode: record
|
||||
|
||||
@@ -40,16 +43,41 @@ Run after a skill run that produced a reusable lesson.
|
||||
| 7 | The token is invalid or lacks permission, so the old rows are unknown. Stop and report the token problem, and create no page |
|
||||
| 8 | Some other API failure. Stop and report that status, and create no page |
|
||||
|
||||
- Update `LEARN_CONTENTS` in the same pass (apply `templates/learn-contents.md`; add the repo row if missing, otherwise refresh its 最後更新時間), branching on its read exactly as above: only exit 4 creates the directory page from the template, while 7 and 8 stop the run instead of rebuilding a directory whose other repos' rows were never read. Touch no row that belongs to another repository.
|
||||
- A failed `jsc-gitea:wiki` write on either page: retry once. Still failing, stop and report which page was not written (`LEARN_{HASH}` or `LEARN_CONTENTS`) together with the row content that was meant to go in, so the lesson is not lost. Never report a page as written when it was not.
|
||||
- Done when the sub agent reports both pages written, the main agent has confirmed the row exists on `LEARN_{HASH}`, and the rows that were there before are still there.
|
||||
- Check the row's link before writing it. Feed the `wiki-url` output to `jsc-gitea/tools/link-check.sh`; it prints one `{OK|DEAD|SKIP}<TAB>{url}<TAB>{note}` line per URL and resolves Gitea URLs through the API, because a private repo answers a logged-out web request with 404 and would fail a page that is there.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | Every link answered. Write the row and the directory block |
|
||||
| 1 | At least one link is DEAD. Write nothing and report the DEAD lines to the caller |
|
||||
| 2 | No URL reached the script. Pass the URLs and rerun |
|
||||
| 3 | The list holds a Gitea URL but `GITEA_HOST` is unset. Set it and rerun; never skip the check |
|
||||
| 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work |
|
||||
|
||||
- Update `LEARN_CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the block work — never hand-edit the directory page. Build one file holding the single H2 block from `templates/learn-contents.md` — the `## LEARN_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the repository name, the absolute link from `gitea.sh wiki-url <LEARN repo> LEARN_{HASH}`, and the update time. Then run:
|
||||
|
||||
`jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 "LEARN_{HASH}" {block file} templates/learn-contents.md`
|
||||
|
||||
The key is the H2 heading itself, the content page name `LEARN_{HASH}`, so it stays the same string across every run and one repository keeps exactly one block. The 教訓紀錄 bullet carries that same page as a link, and that link is what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST`, one move of `JSC_WIKI_REPO_LEARN`, or one difference in how Gitea encodes the page name makes the match fail and appends a second block for the same repository. The page name depends only on `{owner}/{repo}`. The `2` is `<key-col>`, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text (the link text alone) becomes the H2 heading, and a page already in list shape ignores it. The script reads the whole page, replaces the matching block and appends when none matches, so every block that belongs to another repository stays as it was.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | The block is in place. It prints `updated` or `added` plus the page it wrote |
|
||||
| 1 | The page content could not be assembled, or the write failed. Report `LEARN_CONTENTS` as not written, together with the block content. A page with no matching block is not this code: the block is appended instead |
|
||||
| 2 | An argument was rejected. Fix the argument and rerun this bullet; nothing was written |
|
||||
| 3 | No CONTENTS wiki repo is configured. Report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set. The lesson itself is on `LEARN_{HASH}` and stays there |
|
||||
| 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/learn-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 |
|
||||
| 7 | The token is invalid or lacks permission, so the other repositories' blocks are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those blocks alive |
|
||||
| 8 | Some other API failure. Stop and report that status and retry only after the API is back |
|
||||
|
||||
- A failed write on either page: retry once. Still failing, stop and report which page was not written (`LEARN_{HASH}` or `LEARN_CONTENTS`) together with the row or block content that was meant to go in, so the lesson is not lost. Never report a page as written when it was not.
|
||||
- Done when the sub agent reports both pages written, the main agent has confirmed the row exists on `LEARN_{HASH}` and the block on `LEARN_CONTENTS`, and every row and block that was there before is still there.
|
||||
|
||||
## Mode: consult
|
||||
|
||||
Run before a skill run, to apply past lessons.
|
||||
|
||||
1. Resolve `{owner}/{repo}` and compute `{HASH}` as in record mode. Done when `LEARN_{HASH}` is known.
|
||||
2. Read `LEARN_CONTENTS` and the repo's `LEARN_{HASH}` via `jsc-gitea:wiki`, and branch on the exit code `jsc-gitea/tools/gitea.sh` returned. Only 4 means the page is absent; every other failure code means the read never happened, so an empty page must never be inferred from it.
|
||||
2. Read `LEARN_CONTENTS` from the CONTENTS repo and the repo's `LEARN_{HASH}` from the LEARN repo via `jsc-gitea:wiki` — two `wiki-repo` calls, two different wikis — and branch on the exit code `jsc-gitea/tools/gitea.sh` returned. Only 4 means the page is absent; every other failure code means the read never happened, so an empty page must never be inferred from it. The blocks on `LEARN_CONTENTS` point at lesson pages by absolute URL, and blocks for other repositories point outside the LEARN repo resolved here, so follow each link as given rather than treating the H2 heading as a page in this wiki.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
@@ -60,3 +88,23 @@ Run before a skill run, to apply past lessons.
|
||||
|
||||
Done when both pages are read, or reported missing under exit 4, or the run stopped on 5, 7 or 8.
|
||||
3. Surface every row whose 技能 matches the skill about to run, and summarize each matched 下次做法 for the caller to apply. Done when the matched rows (or 「無相符教訓」) are reported.
|
||||
|
||||
## Close: record how the run ended
|
||||
|
||||
Both modes end here, as the very last thing this skill does:
|
||||
|
||||
`jsc-hooks/tools/report-status.sh skill-end jsc-log:learn {status} {exit} "{detail}"`
|
||||
|
||||
Resolve that path the way this file already resolves `jsc-gitea/tools/hash-id` and the other sibling plugin scripts — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A lesson that was recorded stays recorded whether or not the recorder of recorders was installed.
|
||||
|
||||
| status | This skill's case |
|
||||
| --- | --- |
|
||||
| `ok` | record — `LEARN_{HASH}` carries the new row and `LEARN_CONTENTS` this repository's block, and everything that was there before is still there. consult — both pages were read, or a page was reported absent under exit 4, and the matched 下次做法 lines reached the caller |
|
||||
| `blocked` | The run never reached a page: `hash-id` exit 1 (no SHA-1 helper), `wiki-repo LEARN` or `wiki-repo CONTENTS` exit 3 (no wiki repo configured for that page type), or `link-check.sh` exit 3 (`GITEA_HOST` unset) |
|
||||
| `degraded` | record — the lesson row is on `LEARN_{HASH}` but `wiki-contents.sh upsert` did not land the directory block (exit 1, 3, 7 or 8), so the lesson is on the wiki and nothing points at it. consult — one of the two pages was read and the other stopped the run, so the caller got part of the lesson set and knows it |
|
||||
| `failed` | An API call answered with something unexpected after the work started: `link-check.sh` exit 1 on a DEAD link, a `wiki-get` that came back 7 or 8, or a page write that failed its retry as well. Nothing reached `LEARN_{HASH}` |
|
||||
| `aborted` | The user stopped the run, or record mode was called with nothing reusable to record, so the six facts never formed an entry and no write was attempted |
|
||||
|
||||
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the mode plus counts and exit codes, never the lesson text, page names, branch names, or personal data.
|
||||
|
||||
Done when the command has run, or the script was absent and this step was skipped.
|
||||
|
||||
+54
-7
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: report
|
||||
description: Summarise work logs into a yearly, monthly, weekly or daily report. Resolve the period with tools/report-range.sh, resolve the template with tools/report-template.sh - a project's .jsc/templates/report-{period}.md wins over the skill's own copy - then read every log page listed in LOG_CONTENTS and keep the entries dated inside the range. Fill the template with real aggregates (entry count, repositories, elapsed time, token usage, blockers, carry-overs) and write it to wiki REPORT_{HASH}, hashed from {owner}/{repo}/{period}, appending the period as a new section. Use when someone asks for a work summary over a period; not for recording a single work package, which is jsc-log:worklog.
|
||||
description: Summarise work logs into a yearly, monthly, weekly or daily report. Resolve the period with tools/report-range.sh, resolve the template with tools/report-template.sh - a project's .jsc/templates/report-{period}.md wins over the skill's own copy - then read every log page listed in LOG_CONTENTS and keep the entries dated inside the range. Fill the template with real aggregates (entry count, repositories, elapsed time, token usage, blockers, carry-overs) and write it to wiki REPORT_{HASH}, whose full 40-character uppercase hash comes from the REPORT wiki repo's own {owner}/{repo} plus the period rather than from a code repo, appending the period as a new section. Directory pages LOG_CONTENTS, LEARN_CONTENTS and REPORT_CONTENTS all sit in the shared CONTENTS wiki repo while every content page stays in its own type's repo, so the REPORT_CONTENTS entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name REPORT_{HASH}, with a bullet per field and the report page linked by its absolute wiki-url. Use when someone asks for a work summary over a period; not for recording a single work package, which is jsc-log:worklog.
|
||||
---
|
||||
|
||||
# report — summarise work logs by period
|
||||
@@ -23,11 +23,15 @@ Done when start, end and label are known.
|
||||
|
||||
## 2. Resolve and collect
|
||||
|
||||
Directory pages and content pages no longer share a wiki. Every `*_CONTENTS` page — `LOG_CONTENTS`, `LEARN_CONTENTS`, `REPORT_CONTENTS` — lives in the one repo that `jsc-gitea/tools/gitea.sh wiki-repo CONTENTS` resolves (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3; it never falls back to a page type's own variable). Each content page still lives in its own type's repo: log pages in `wiki-repo LOG`, lesson pages in `wiki-repo LEARN`, the report page in `wiki-repo REPORT`. Keep the two apart — one shared directory repo, one repo per content type — and resolve every one of them on its own.
|
||||
|
||||
Every directory page is a list page, not a table: an H1, a `>` preamble, then one H2 block per entry whose heading is that entry's content page name, with `- {欄位名}:{值}` bullets under it. Read the links out of the bullets.
|
||||
|
||||
Run these four lines of work in parallel — none of them consumes another's output, and the log pages are the slow one:
|
||||
|
||||
1. **Template.** `tools/report-template.sh resolve {period}` from the working directory prints `{path}<TAB>{project|skill}`.
|
||||
2. **Log pages.** `jsc-gitea/tools/gitea.sh wiki-repo LOG`, then read `LOG_CONTENTS` through `jsc-gitea:wiki`, then read **every** log page it lists, one sub agent per page.
|
||||
3. **Lessons (yearly only).** `gitea.sh wiki-repo LEARN`, then read `LEARN_CONTENTS` through `jsc-gitea:wiki` for the 全年教訓 section. Resolve `JSC_WIKI_REPO_LEARN` on its own: the LOG repo resolved in line 2 never stands in for it, and LOG and LEARN pages routinely live in different wiki repos. Other periods skip this line.
|
||||
2. **Log pages.** `gitea.sh wiki-repo CONTENTS`, then read `LOG_CONTENTS` through `jsc-gitea:wiki`, then read **every** log page it lists, one sub agent per page. The page holds one H2 block per log page, each with a 日誌頁 bullet carrying an absolute URL, so follow each link as given rather than the H2 heading; `gitea.sh wiki-repo LOG` names the repo the log pages of this working directory sit in, and a block pointing elsewhere is another repo's log page, not a broken link.
|
||||
3. **Lessons (yearly only).** Read `LEARN_CONTENTS` from the same CONTENTS repo, then read the lesson pages its blocks link for the 全年教訓 section. Resolve the lesson pages' own repo with `gitea.sh wiki-repo LEARN`, never with the LOG repo of line 2: the two directory pages now share a repo, but LOG and LEARN **content** pages routinely live in different ones, and reusing the LOG repo reads the wrong wiki. Other periods skip this line.
|
||||
4. **Report repo.** `gitea.sh wiki-repo REPORT`, so step 3 has its target ready.
|
||||
|
||||
Exit branches for the external calls above:
|
||||
@@ -37,6 +41,7 @@ Exit branches for the external calls above:
|
||||
| `report-template.sh resolve` | 0 | Use the path; name the `project` or `skill` source in the final report. `project` means the working directory holds `.jsc/templates/report-{period}.md` and that file wins — the same period rendered from two templates has to be traceable to the file that shaped it |
|
||||
| `report-template.sh resolve` | 2 | Period name or start directory rejected. Rerun from the working directory with the period from step 1 |
|
||||
| `report-template.sh resolve` | 3 | Neither the project copy nor the skill's own copy exists. Stop and report that `templates/report-{period}.md` is missing from the plugin; do not invent a layout |
|
||||
| `gitea.sh wiki-repo CONTENTS` | 3 | Stop and report that no wiki repo is configured for the directory pages, naming `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO`. Ask per the `jsc-ask:ask` rules, then rerun. Without `LOG_CONTENTS` there is no list of log pages to read |
|
||||
| `gitea.sh wiki-repo LOG` | 3 | Stop and report that no wiki repo is configured for LOG, naming `JSC_WIKI_REPO_LOG` and `JSC_WIKI_REPO`. Ask per the `jsc-ask:ask` rules, then rerun. Without log pages there is nothing to summarise |
|
||||
| `gitea.sh wiki-repo LEARN` | 3 | Fill the 全年教訓 section with 無 and say the LEARN wiki repo is unset. The rest of the yearly report still stands |
|
||||
| `gitea.sh wiki-repo REPORT` | 3 | Carry on collecting; step 3 handles the skipped write |
|
||||
@@ -68,19 +73,43 @@ Follow the template's headings and tables exactly, including ones with no data:
|
||||
|
||||
Write through `jsc-gitea:wiki`:
|
||||
|
||||
- Repo: the REPORT repo from step 2, line 4.
|
||||
- Page: `REPORT_` plus `gitea.sh hash-id "{owner}/{repo}/{period}"`, where `{owner}/{repo}` is the REPORT wiki repo. Year, month, week and day each get their own page.
|
||||
- Repo: the REPORT repo from step 2, line 4. It hosts the content page only; `REPORT_CONTENTS` goes to the CONTENTS repo instead.
|
||||
- Page: `REPORT_` plus `gitea.sh hash-id "{owner}/{repo}/{period}"`. Here `{owner}/{repo}` is **the REPORT wiki repo itself** — the value `gitea.sh wiki-repo REPORT` printed — and not the code repo the logs came from. Every other page in this skill set hashes the code repo; this one page does not, because a report spans every code repo whose logs landed in the range, so no single code repo names it. Feed `hash-id` the exact string `{REPORT wiki owner}/{REPORT wiki repo}/{period}`, with `{period}` being the literal `daily`, `weekly`, `monthly` or `yearly` — so year, month, week and day each get their own page. `hash-id` prints the full 40-character uppercase SHA-1: use it whole, never shortened and never prefixed.
|
||||
- Read the page first and branch on the exit code the underlying `gitea.sh wiki-get` returned. **Only exit 4 means the page is not there yet** and may be built from scratch. On exit 0 the existing sections are in hand, so append into them. On exit 7 the token is invalid or lacks permission, and on exit 8 the API failed some other way: both leave the earlier periods unknown, so stop, report the status and write nothing — a page rebuilt on top of an unread read loses every period already on it.
|
||||
- Check every link before it goes on a page — the ones inside the new section and the directory block's link alike: `jsc-gitea/tools/link-check.sh {url}...`. It prints one `{OK|DEAD|SKIP}<TAB>{url}<TAB>{note}` line per URL and resolves Gitea URLs through the API, because a private repo answers a logged-out web request with 404 and would fail a page that is there. Only exit 0 permits the write.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | Every link answered. Write the section, or the directory block |
|
||||
| 1 | At least one link is DEAD. Write nothing and report the DEAD lines to the caller |
|
||||
| 2 | No URL reached the script. Pass the URLs and rerun |
|
||||
| 3 | The list holds a Gitea URL but `GITEA_HOST` is unset. Set it and rerun; never skip the check |
|
||||
| 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work |
|
||||
|
||||
- Append this period as a new section, newest first. Rerunning the same period replaces that period's section only, leaving the other periods untouched.
|
||||
- Refresh the page's row in `REPORT_CONTENTS` from `templates/report-contents.md`: add the row if missing, otherwise refresh its 最新一期、期數 and 最後更新. Its read branches the same way — only exit 4 creates the directory page from the template, while 7 and 8 stop the run. Touch no row that belongs to another report page.
|
||||
- Refresh the page's block in `REPORT_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh` — never hand-edit the directory page. It sits in the CONTENTS repo, not the REPORT repo, so the 報表頁 bullet links the report page as `[REPORT_{HASH}](<url>)`, with `<url>` from `gitea.sh wiki-url <REPORT repo> REPORT_{HASH}`. Every link on the report body and in this block takes that same `[{text}]({absolute URL})` shape; the same-wiki `[[...]]` form resolves inside one wiki only and dead-links from here without reporting an error. Build one file holding the single H2 block from `templates/report-contents.md` — the `## REPORT_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the absolute link, the bare `{HASH}`, the period, the newest label, the section count and the update time. Then run:
|
||||
|
||||
`jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 "REPORT_{HASH}" {block file} templates/report-contents.md`
|
||||
|
||||
The key is the H2 heading itself, the content page name `REPORT_{HASH}` this step already computed, and the 報表頁 bullet carries that same page as a link for a human to click. That link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST` or one difference in how Gitea encodes the page name makes this run's text differ from the last run's, the match fails, the block is appended, and the same report page now owns two blocks of which the older is never updated again. The page name depends only on the hashed `{owner}/{repo}/{period}`, so neither of those two touches it. The `1` is `<key-col>`, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text (the link text alone) becomes the H2 heading, and a page already in list shape ignores it. The script replaces the matching block and appends when none matches, so every block that belongs to another report page stays as it was.
|
||||
|
||||
| Call | Exit | Do |
|
||||
| --- | --- | --- |
|
||||
| `gitea.sh wiki-repo REPORT` | 3 | Print the finished report and say the write was skipped because no wiki repo is configured for REPORT. The report itself is still the deliverable |
|
||||
| `gitea.sh hash-id` | 1 | No SHA-1 helper on this machine. Stop and report that `sha1sum` or `shasum` has to be installed. Never hand-compute the hash |
|
||||
| `gitea.sh hash-id` | 2 | The input was empty, which means the REPORT wiki repo or the period never reached it. Fix the string and rerun; the empty string has a valid SHA-1 and would file the report on a page nobody reads |
|
||||
| `gitea.sh wiki-url` | 4 / 5 | 4 means the report page write has not landed, so write it first; 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand |
|
||||
| `gitea.sh wiki-url` | 7 / 8 | 7 means the token is invalid or lacks permission (HTTP 401/403), 8 means some other API failure. Both leave it unknown whether the page is there, so stop and report the token or API status. Never fold either into 4: reading an invalid key as a missing page is the same misread this table separates 7 from 4 to prevent, and here it would send the run back to rewrite a report page that is already on the server |
|
||||
| `jsc-gitea:wiki` write | failure | Retry once. Still failing, stop and report the page name that was not written, and print the report body so the work is not lost. Never report a page as written when it was not |
|
||||
| `wiki-contents.sh upsert` | 0 | The block is in place. It prints `updated` or `added` plus the page it wrote |
|
||||
| `wiki-contents.sh upsert` | 1 | The page content could not be assembled, or the write failed. Report `REPORT_CONTENTS` as not written, together with the block content. A page with no matching block is not this code: the block is appended instead |
|
||||
| `wiki-contents.sh upsert` | 2 | An argument was rejected. Fix the argument and rerun this bullet; nothing was written |
|
||||
| `wiki-contents.sh upsert` | 3 | No CONTENTS wiki repo is configured. Report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set. The report itself is on `REPORT_{HASH}` and stays there |
|
||||
| `wiki-contents.sh upsert` | 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/report-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 |
|
||||
| `wiki-contents.sh upsert` | 7 | The token is invalid or lacks permission, so the other blocks are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those blocks alive |
|
||||
| `wiki-contents.sh upsert` | 8 | Some other API failure. Stop and report that status and retry only after the API is back |
|
||||
|
||||
Write the content page before its row in `REPORT_CONTENTS`, never the two at once: a directory row pointing at a page whose write failed is worse than a missing row.
|
||||
Write the content page before its block in `REPORT_CONTENTS`, never the two at once: a directory block pointing at a page whose write failed is worse than a missing block, and `wiki-url` cannot name a page that is not there yet.
|
||||
|
||||
Done when the page URL is reported, or the skipped write is reported with its reason, or the run stopped on a read that returned 7 or 8 and that status was reported.
|
||||
|
||||
@@ -89,3 +118,21 @@ Done when the page URL is reported, or the skipped write is reported with its re
|
||||
State the period label, entry count, repositories covered, template source, and the page URL. Name every unfinished work package that carried over — that list is what the next period starts from. State `ELAPSED_MISSING` and `TOKEN_MISSING` whenever either is above 0, so a small total is read as missing data rather than a light week.
|
||||
|
||||
Done when those five facts, the carry-over list and the two missing-data counts are stated.
|
||||
|
||||
Then record how the run ended, as the very last thing this skill does:
|
||||
|
||||
`jsc-hooks/tools/report-status.sh skill-end jsc-log:report {status} {exit} "{detail}"`
|
||||
|
||||
Resolve that path the way this file already resolves `jsc-gitea/tools/link-check.sh` and the other sibling plugin scripts — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A report that was produced stays produced whether or not the recorder was installed.
|
||||
|
||||
| status | This skill's case |
|
||||
| --- | --- |
|
||||
| `ok` | The period's section is on `REPORT_{HASH}` and the `REPORT_CONTENTS` block carries this period. `log-aggregate.sh` exit 3 stays `ok`: an empty range is an answer, and section 2 requires the report to be produced anyway — put `ENTRIES=0` in `{detail}` so the zero is read as a counted zero, not a run that quit |
|
||||
| `blocked` | Nothing could be summarised and nothing was: `report-range.sh` exit 4 (this machine's `date` does no date arithmetic), `report-template.sh resolve` exit 3 (neither template exists), `wiki-repo CONTENTS` or `wiki-repo LOG` exit 3 (no directory or log pages to read), `hash-id` exit 1, or `link-check.sh` exit 3 or 7 |
|
||||
| `degraded` | The report body is finished and handed to the caller but did not fully land: `wiki-repo REPORT` exit 3 skipped the wiki write entirely, or the section landed and `wiki-contents.sh upsert` did not, or `wiki-repo LEARN` exit 3 left the yearly 全年教訓 section filled with 無. Say which part is missing in `{detail}` |
|
||||
| `failed` | The collection or the write broke part-way: `link-check.sh` exit 1 on a DEAD link, a `jsc-gitea:wiki` read or a `wiki-url` call that came back 7 or 8, `log-aggregate.sh` exit 4 on an unreadable page file, or a write that failed its retry as well |
|
||||
| `aborted` | The user stopped the run, most often at the period question in section 1, so no range was ever fixed and no page was read |
|
||||
|
||||
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the period plus counts and exit codes, never report text, page names, branch names, or personal data.
|
||||
|
||||
Done when the command has run, or the script was absent and this step was skipped.
|
||||
|
||||
@@ -25,3 +25,20 @@ Data is recorded continuously by `jsc-hooks/hooks/skill-usage.sh` under `$JSC_HO
|
||||
| 2 | The subcommand or the `--cli` argument was rejected — the subcommand is neither `skills` nor `chains`, or `--cli` came with no value. Fix the argument and rerun. Never rerun the same command unchanged, and never report the counts as zero: nothing was read |
|
||||
|
||||
Done when the exit code was read and the branch it names was taken.
|
||||
2. Record how the run ended, as the very last thing this skill does:
|
||||
|
||||
`jsc-hooks/tools/report-status.sh skill-end jsc-log:stats {status} {exit} "{detail}"`
|
||||
|
||||
Resolve that path the way this file already names `jsc-hooks/hooks/skill-usage.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. This skill only reads counts; it must never fail because a count of its own could not be written.
|
||||
|
||||
| status | This skill's case |
|
||||
| --- | --- |
|
||||
| `ok` | The tool exited 0 and every printed line reached the report. A run that printed no line is `ok` as well — the count really is zero, so say `count=0` in `{detail}` rather than dressing an empty data file up as a problem |
|
||||
| `blocked` | `tools/usage-stats.sh` is not on this machine, which is a partial plugin install rather than a zero count. No number was ever read, so none is reported |
|
||||
| `degraded` | The caller asked for both counts and only one sub-command answered, so the report covers half of what was asked. Name the half that is missing in `{detail}` |
|
||||
| `failed` | Exit 2 — the sub-command or the `--cli` value was rejected, so nothing was read. This is the one case that must never be reported as a count of zero, and recording it as `failed` is what keeps the two apart in the event stream too |
|
||||
| `aborted` | The user stopped the run, or the request turned out to be about elapsed time or token usage, which belong to `jsc-log:worklog`; this skill then stops before it counts anything |
|
||||
|
||||
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the sub-command plus counts and exit codes, never branch names or personal data.
|
||||
|
||||
Done when the command has run, or the script was absent and this step was skipped.
|
||||
|
||||
+69
-13
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: worklog
|
||||
description: Append one work-log entry to wiki LOG_{HASH} plus LOG_CONTENTS as soon as a task ends, where a task is one work package, one round of PR-comment fixes, or one standalone fix commit — one task, one entry, appended to the same page. Every entry carries the ten facts (repo, branch, plan link, work package link, elapsed time from session-timer, token usage per CLI, status, details, difficulties, PR target); HASH follows the shared 8-char rule with the H-prefix fallback and the work-week Friday drives the page content. Merge whatever tools/worklog-pending.sh holds for that HASH into the same write, then clear the pending area once that write succeeded. Trigger at the end of every such task in implement or maintain; not for planning notes.
|
||||
description: Append one work-log entry to wiki LOG_{HASH} plus LOG_CONTENTS as soon as a task ends, where a task is one work package, one round of PR-comment fixes, or one standalone fix commit — one task, one entry, appended to the same page. Every entry carries the ten facts (repo, branch, plan link, work package link, elapsed time from session-timer, token usage per CLI, status, details, difficulties, PR target); HASH is the full 40-character uppercase SHA-1 of {owner}/{repo} and the work-week Friday drives the page content. LOG_{HASH} sits in the LOG wiki repo while LOG_CONTENTS sits in the separate CONTENTS repo, so the directory entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name LOG_{HASH}, with a bullet per field and the log page linked by its absolute wiki-url. Merge whatever tools/worklog-pending.sh holds for that HASH into the same write, then clear the pending area once that write succeeded. Trigger at the end of every such task in implement or maintain; not for planning notes.
|
||||
---
|
||||
|
||||
# worklog — work log
|
||||
@@ -27,36 +27,43 @@ Rows 3, 4, 5 and 6 each hit a different source and none of them reads another's
|
||||
| --- | --- | --- |
|
||||
| 1 | Repository name | Parse `{owner}/{repo}` from `git remote get-url origin`. This is the code repo — never pass it to `wiki-url`, which takes the wiki-hosting repo |
|
||||
| 2 | Branch name | `git branch --show-current` |
|
||||
| 3 | Plan name | Absolute link to the plan page: `[PLAN_{HASH}](<url>)`. Resolve the hosting repo with `jsc-gitea/tools/gitea.sh wiki-repo PLAN`, then take `<url>` from `gitea.sh wiki-url <that repo> PLAN_{HASH}` — PLAN and LOG may live in different wiki repos, and `[[...]]` only resolves inside one wiki. `wiki-repo` exit 3 (no wiki repo configured for that type) or `wiki-url` exit 4 (page not found) → fill the literal 「無」 for this row and carry on; a `worklog` run triggered from `maintain` normally has no plan page. `wiki-url` exit 5 → stop and report that the page carries no `html_url`; never assemble the URL by hand. `wiki-url` exit 7 (token invalid or no permission, HTTP 401/403) or exit 8 (other API failure) → stop and report the token or API status; never fill 「無」, because that records a page that exists as a page that does not |
|
||||
| 4 | Work package id | Absolute link to the work package heading: `[WP-xx](<url>#wp-xx)`. Resolve the hosting repo with `gitea.sh wiki-repo ANALYZE`, then take `<url>` from `gitea.sh wiki-url <that repo> ANALYZE_{HASH}`. A comment-fix round links to the same work package it belongs to. Same branching as row 3: `wiki-repo` exit 3 or `wiki-url` exit 4 → fill 「無」 and carry on; `wiki-url` exit 5 → stop and report that the page carries no `html_url`, never assemble the URL by hand; `wiki-url` exit 7 or 8 → stop and report the token or API status, never fill 「無」 |
|
||||
| 3 | Plan name | Link to the plan page in the shape `[PLAN_{HASH}](<url>)`. Resolve the hosting repo with `jsc-gitea/tools/gitea.sh wiki-repo PLAN`, then take `<url>` from `gitea.sh wiki-url <that repo> PLAN_{HASH}` — never assemble it by hand, and never use the same-wiki `[[...]]` form: PLAN and LOG may live in different wiki repos, and `[[...]]` resolves inside one wiki only. `wiki-repo` exit 3 (no wiki repo configured for that type) or `wiki-url` exit 4 (page not found) → fill the literal 「無」 for this row and carry on; a `worklog` run triggered from `maintain` normally has no plan page. `wiki-url` exit 5 → stop and report that the page carries no `html_url`; never assemble the URL by hand. `wiki-url` exit 7 (token invalid or no permission, HTTP 401/403) or exit 8 (other API failure) → stop and report the token or API status; never fill 「無」, because that records a page that exists as a page that does not |
|
||||
| 4 | Work package id | Link to the work package heading in the shape `[WP-xx](<url>#wp-xx)`. Resolve the hosting repo with `gitea.sh wiki-repo ANALYZE`, then take `<url>` from `gitea.sh wiki-url <that repo> ANALYZE_{HASH}`. A comment-fix round links to the same work package it belongs to. Same branching as row 3: `wiki-repo` exit 3 or `wiki-url` exit 4 → fill 「無」 and carry on; `wiki-url` exit 5 → stop and report that the page carries no `html_url`, never assemble the URL by hand; `wiki-url` exit 7 or 8 → stop and report the token or API status, never fill 「無」 |
|
||||
| 5 | Elapsed time | `jsc-hooks/hooks/session-timer.sh report {session_id}` prints `{sid} {seconds}` and always exits 0. Convert the seconds to h/m and read it at the moment the task ends, so it counts only this task. `0` means the timer holds no start record for that session, not a task that took no time: fill the literal 「無資料」 and say the timer had no record. Never estimate the duration from the transcript |
|
||||
| 6 | Token usage | `tools/token-usage.sh <cli> {session_id}` per CLI that ran; it prints `input<TAB>output`. Pass the same `{session_id}` as row 5 so the elapsed time and the token count describe one task. Fill `N/A` in both columns when it prints `N/A`; exit 2 means the CLI name is not one of claude / codex / copilot / antigravity / kiro, so fix the name and rerun |
|
||||
| 7 | Task status | One of the literal values 「完成」, 「部分完成」, 「阻塞」 (with reason when blocked). Derive it from the session when the session shows it; otherwise ask via `jsc-ask:ask`, offering those three literals as the options and stating each option's impact scope (「完成」 closes the task, 「部分完成」 leaves the remainder open for the next run, 「阻塞」 records the blocker and hands it back to the operator) |
|
||||
| 8 | Details and outputs | One line per changed file or produced page: what changed there and why. A round that changed nothing says what was tried and why it was dropped |
|
||||
| 9 | Difficulties and resolutions | One pair per line. Ask via `jsc-ask:ask` when the session does not show them |
|
||||
| 10 | PR target branch | Link to the PR page |
|
||||
| 10 | PR target branch | Link to the PR page in the shape `[{base branch}]({PR URL})` |
|
||||
|
||||
The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`.
|
||||
Every link in an entry is text plus an absolute URL — `[{text}]({url})` — with wiki URLs coming from `gitea.sh wiki-url`. The same-wiki `[[...]]` form is out: it resolves inside one wiki only, and a link that fails that way looks like ordinary text on the page instead of reporting an error.
|
||||
|
||||
The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`, which prints the full 40-character uppercase SHA-1 of its input — no truncation to 8 characters and no prefix rewrite. A page name shortened by hand points at a page nobody else writes to.
|
||||
|
||||
## Write the entry
|
||||
|
||||
1. Resolve the LOG wiki repo and the entry's `{HASH}` **in parallel** — neither needs the other.
|
||||
- Repo: `gitea.sh wiki-repo LOG`. Exit 3 means no LOG wiki repo is configured; hand that to `jsc-gitea:wiki`, which owns the resolution order and the question to ask. Exit 2 means the type argument was misspelled, so fix it and rerun.
|
||||
- Hash: `jsc-gitea/tools/hash-id "{owner}/{repo}"` on the code repo from row 1. Exit 1 means this machine has no SHA-1 helper: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash.
|
||||
- Repo: `gitea.sh wiki-repo LOG`. This repo hosts the content page `LOG_{HASH}` only. The directory page `LOG_CONTENTS` lives in a different repo and is resolved in step 6, so never reuse this value for it. Exit 3 means no LOG wiki repo is configured; hand that to `jsc-gitea:wiki`, which owns the resolution order and the question to ask. Exit 2 means the type argument was misspelled, so fix it and rerun.
|
||||
- Hash: `jsc-gitea/tools/hash-id "{owner}/{repo}"` on the code repo from row 1. Exit 1 means this machine has no SHA-1 helper: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash. Exit 2 means the input was empty, which happens when row 1 failed to parse `{owner}/{repo}`: fix row 1 and rerun, because the empty string has a valid SHA-1 and would file this entry on a page nobody reads.
|
||||
|
||||
Done when the hosting `{owner}/{repo}` and the 8-character `{HASH}` are both known.
|
||||
2. Run `tools/worklog-target.sh "{HASH}" all` and `tools/worklog-target.sh friday` in parallel. `all` prints `PAGE=LOG_{HASH}` and `CONTENTS=LOG_CONTENTS`; `friday` prints the Friday of the current work week as `yyyy-MM-dd`, which drives the page content and the row dates. Exit 2 means the arguments were rejected, so fix them and rerun. Exit 4 from `friday` means this machine's `date` does no date arithmetic: stop and report it, because a hand-picked Friday is exactly what goes wrong across a month or year boundary. Done when both page names and that Friday date are known.
|
||||
Done when the hosting `{owner}/{repo}` and the full 40-character uppercase `{HASH}` are both known.
|
||||
2. Run `tools/worklog-target.sh "{HASH}" all` and `tools/worklog-target.sh friday` in parallel. `all` prints `PAGE=LOG_{HASH}` and `CONTENTS=LOG_CONTENTS`; `friday` prints the Friday of the current work week as `yyyy-MM-dd`, which drives the page content and the directory block's dates. Exit 2 means the arguments were rejected, so fix them and rerun. Exit 4 from `friday` means this machine's `date` does no date arithmetic: stop and report it, because a hand-picked Friday is exactly what goes wrong across a month or year boundary. Done when both page names and that Friday date are known.
|
||||
3. Fill `templates/log-entry.md` with the ten facts of this one task and save it to a file. Done when that file holds exactly one entry.
|
||||
4. Run `tools/worklog-pending.sh merge {HASH} {entry file}`. It prints `MERGED=` (pending content in time order, then this task's entry), `CLAIM=` (the pending files it took) and `PENDING=` (how many). Pending content was written by an earlier stage that ended without a work log, so it belongs in **this** write.
|
||||
4. Run `tools/worklog-pending.sh merge {HASH} {entry file}`. It prints `MERGED=` (pending content in time order, then this task's entry), `CLAIM=` (the pending files it took) and `PENDING=` (how many). Pending content was written by an earlier stage that ended without a work log, so it belongs in **this** write. `merge` also picks up the legacy `H` + first 7 characters directory of the same `{HASH}`, so pending content stored under the previous hash rule still reaches the wiki.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | Carry on with `MERGED` and `CLAIM`. `PENDING=0` is normal and still exit 0 |
|
||||
| 1 | A read or write under `$JSC_HOME/worklog-pending` failed. Stop and report the path from the message; nothing was deleted, so a rerun loses nothing |
|
||||
| 2 | The `{HASH}` is not 8 uppercase alphanumerics, or the entry file argument is missing. Fix the argument and rerun from step 1 |
|
||||
| 2 | The `{HASH}` is none of the three accepted shapes — 40 uppercase hex characters, 8 uppercase hex characters, or `H` plus 7 uppercase hex characters — or the entry file argument is missing. The last two are old pending directories left by the previous hash rule and are accepted only until the migration finishes. Pass the value `hash-id` printed, unshortened, and rerun from step 1 |
|
||||
|
||||
Done when `MERGED` and `CLAIM` are known.
|
||||
5. Read `PAGE` via `jsc-gitea:wiki`, and branch on the exit code the underlying `gitea.sh wiki-get` returned. **Only exit 4 means the page is not there yet.** Reading any other code as "it does not exist" builds a fresh page from `templates/log-entry.md` and appends to that — which replaces the whole existing work log with this one entry, and no entry on it can be recovered from the wiki afterwards.
|
||||
4.5. Run `tools/worklog-pending.sh orphans`. It takes no `{HASH}` and scans the whole pending area for directories that are not 40 uppercase hex characters. Exit 0 means there are none, so carry on silently. Exit 4 prints one `{directory}<TAB>{file count}<TAB>{path}` line per orphan: report every line to the user and carry on with this run — an orphan belongs to some other repository, so it never blocks this one.
|
||||
|
||||
Why this runs at all: a directory the current rule cannot address holds work-log entries that no run will ever write, and nothing else reports them. "No pending content" and "pending content stranded under a name this rule cannot address" both reach the caller as success, so without this scan the entries stay invisible until somebody reads the directory by hand. Step 4 already adopts the one legacy shape that can be derived from `{HASH}`; this scan covers every shape that cannot.
|
||||
|
||||
Done when the scan exited 0, or its lines were reported to the user.
|
||||
5. Read `PAGE` from the LOG wiki repo of step 1 via `jsc-gitea:wiki`, and branch on the exit code the underlying `gitea.sh wiki-get` returned. **Only exit 4 means the page is not there yet.** Reading any other code as "it does not exist" builds a fresh page from `templates/log-entry.md` and appends to that — which replaces the whole existing work log with this one entry, and no entry on it can be recovered from the wiki afterwards.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
@@ -65,8 +72,40 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`
|
||||
| 7 | The key is invalid or lacks permission, so the old content is unknown. Stop and report the key problem, write nothing and create no page — a page created here would take the place of a log that is still on the server |
|
||||
| 8 | Some other API failure. Stop and report that status, write nothing and create no page. Retry only after the API is back |
|
||||
|
||||
Before the write, hand every URL that `MERGED` carries — plan page, work package heading, PR page — to `jsc-gitea/tools/link-check.sh {url}...`. It prints one `{OK|DEAD|SKIP}<TAB>{url}<TAB>{note}` line per URL and resolves Gitea URLs through the API, because a private repo answers a logged-out web request with 404 and would fail a page that is there. Only exit 0 permits the write.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | Every link answered. Write the entries |
|
||||
| 1 | At least one link is DEAD. Write nothing, report the DEAD lines, and go to step 7 as a failure so the pending content survives |
|
||||
| 2 | No URL reached the script. Pass the URLs and rerun |
|
||||
| 3 | The list holds a Gitea URL but `GITEA_HOST` is unset. Set it and rerun; never skip the check |
|
||||
| 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work |
|
||||
|
||||
A failed write stops the run and goes to step 7 as a failure — never report the page as written when it was not. Done when every entry in `MERGED` is on `PAGE`, every entry that was already there is still there, and any 4 / 7 / 8 branch was followed as stated.
|
||||
6. Update `CONTENTS` in the same pass (apply `templates/log-contents.md`; add the row if missing, otherwise refresh its 條目數 and 最後更新). Its read branches exactly as step 5 does: only exit 4 creates the directory page from the template, while 7 and 8 stop the run rather than rebuild a directory whose other rows were never read. Touch no row that belongs to another page. Done when the row for `PAGE` carries this week's Friday date from step 2 and every other row is unchanged.
|
||||
6. Update `CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the block work — never hand-edit the directory page.
|
||||
|
||||
`LOG_CONTENTS` lives in the CONTENTS wiki repo that `gitea.sh wiki-repo CONTENTS` resolves (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`), which is **not** the LOG repo of step 1 and never falls back to it. Because the two pages sit in different wikis, the 日誌頁 bullet links the log page as `[LOG_{HASH}](<url>)`, with `<url>` from `gitea.sh wiki-url <LOG repo> LOG_{HASH}` — never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from here without reporting an error. `wiki-url` exit 4 means the step 5 write has not landed yet, so stop and rerun step 5 before this one; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status.
|
||||
|
||||
Put that URL through the step 5 `link-check.sh` gate before the upsert, with the same exit branches: only exit 0 writes the block, and a DEAD line stops the write and goes to step 7 as a failure.
|
||||
|
||||
Build one file holding the single H2 block from `templates/log-contents.md` — the `## LOG_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the absolute link, the bare `{HASH}` of step 1, this week's Friday date from step 2, the entry count and the update time. Then run:
|
||||
|
||||
`jsc-gitea/tools/wiki-contents.sh upsert LOG 1 "LOG_{HASH}" {block file} templates/log-contents.md`
|
||||
|
||||
The key is the H2 heading itself, the content page name `LOG_{HASH}`, and the 日誌頁 bullet carries that same page as a link for a human to click. That link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST`, one move of `JSC_WIKI_REPO_LOG`, or one difference in how Gitea encodes the page name makes this run's text differ from the last run's, the match fails, the block is appended, and the same log page now owns two blocks of which the older is never updated again. The page name depends only on `{owner}/{repo}`, so none of those three touches it. The `1` in the command is `<key-col>`, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text becomes the H2 heading, and a page already in list shape ignores it. The script reads the whole page, replaces the matching block and appends when none matches, so every block that belongs to another log page stays as it was.
|
||||
|
||||
| Exit | Do |
|
||||
| --- | --- |
|
||||
| 0 | The block is in place. It prints `updated` or `added` plus the page it wrote — carry that word into the close-out |
|
||||
| 1 | The page content could not be assembled, or the write failed. Stop and report it as a failed write, and go to step 7 as a failure. A page with no matching block is not this code: the block is appended instead |
|
||||
| 2 | An argument was rejected (unknown type, key column, missing block file). Fix the argument and rerun this step; nothing was written |
|
||||
| 3 | No CONTENTS wiki repo is configured. Stop and report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set, and go to step 7 as a failure. The log entry itself is on `PAGE` and stays there |
|
||||
| 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/log-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 |
|
||||
| 7 | The key is invalid or lacks permission, so the other blocks are unknown. Stop and report the key problem; the script wrote nothing, which is what keeps the other pages' blocks alive |
|
||||
| 8 | Some other API failure. Stop and report that status and retry only after the API is back |
|
||||
|
||||
Done when the run exited 0 and the block for `PAGE` carries this week's Friday date from step 2, or a non-zero code was reported and step 7 ran as a failure.
|
||||
7. Close the pending area on the result of steps 5 and 6: `tools/worklog-pending.sh commit {HASH} {CLAIM}` after both succeeded, or `tools/worklog-pending.sh abort {HASH} {CLAIM}` after either failed. `abort` keeps every pending file for the retry, so keep the entry file too and rerun from step 4.
|
||||
|
||||
| Exit | Do |
|
||||
@@ -76,3 +115,20 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`
|
||||
| 2 | The claim path is not the one `merge` produced, or it points outside this `{HASH}`'s pending directory. Rerun from step 4 with the `CLAIM` value that `merge` printed; never pass a hand-written path |
|
||||
|
||||
Done when one of the two ran and its exit code was reported.
|
||||
8. Record how this run ended, as the very last thing this skill does:
|
||||
|
||||
`jsc-hooks/tools/report-status.sh skill-end jsc-log:worklog {status} {exit} "{detail}"`
|
||||
|
||||
Resolve that path the way row 5 already resolves `jsc-hooks/hooks/session-timer.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A run whose result could not be recorded still had that result, and a work log that fails because the recorder is absent is worse than no recording.
|
||||
|
||||
| status | This skill's case |
|
||||
| --- | --- |
|
||||
| `ok` | Steps 5 and 6 both wrote and step 7 cleared the pending area. `orphans` exit 4 stays `ok`: an orphan directory belongs to some other repository and changes nothing about this entry — carry its line count in `{detail}` so the count is on record even though the run passed |
|
||||
| `blocked` | Nothing could be written and nothing was: `hash-id` exit 1 (no SHA-1 helper on this machine), `worklog-target.sh friday` exit 4 (no date arithmetic), `wiki-repo LOG` exit 3 (no LOG wiki repo configured), or `link-check.sh` exit 3 (`GITEA_HOST` unset) or exit 7 (token rejected). The gate stopped the run before a page was touched |
|
||||
| `degraded` | The entry is on `LOG_{HASH}` but the close-out is short: `wiki-contents.sh upsert` returned non-zero so `LOG_CONTENTS` still carries the old block, or `worklog-pending.sh commit` exited 1 so the pending files survive a successful write and the next run merges them again |
|
||||
| `failed` | Nothing reached the wiki after the run started working: `link-check.sh` exit 1 stopped the write on a DEAD link, the `PAGE` read came back 7 or 8, or `worklog-pending.sh merge` exited 1 |
|
||||
| `aborted` | The user stopped the run, or the trigger turned out not to hold — no task ended here, so there is no entry to write and none was attempted |
|
||||
|
||||
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: counts and exit codes only, never entry text, page names, branch names, or personal data.
|
||||
|
||||
Done when the command has run, or the script was absent and this step was skipped.
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
# 教訓目錄
|
||||
|
||||
> 由 `jsc-log:learn` 維護。這是教訓目錄頁 `LEARN_CONTENTS`。每個存取庫一列;`LEARN_{HASH}` 的 `{HASH}` 依共用 wiki hash 規則產生:先取 `{owner}/{repo}` 的 SHA-1 前 8 碼並轉成大寫;首碼若是 `0-9`、`A`、`B`、`C`,改用 `H` 加上原前 7 碼,總長維持 8 碼。
|
||||
> 寫入語意:一列代表一個存取庫。先讀整頁,找得到該存取庫既有的那一列就更新那一列,找不到才新增一列。
|
||||
> 禁止整頁覆蓋,也不得改動別人的列。
|
||||
> 由 `jsc-log:learn` 維護。這是教訓目錄頁 `LEARN_CONTENTS`。每個存取庫一個區塊;`LEARN_{HASH}` 的 `{HASH}` 依共用 wiki hash 規則產生:取 `{owner}/{repo}` 的完整 SHA-1 四十碼,a-f 轉大寫,不截短、不加前綴。
|
||||
> 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,教訓頁 `LEARN_{HASH}` 落在 `JSC_WIKI_REPO_LEARN` 的存取庫,兩者分屬不同 wiki。
|
||||
> 連結寫法:教訓紀錄那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。
|
||||
> 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。
|
||||
> 寫入語意:一個區塊代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 LEARN_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該存取庫既有的那個區塊就換掉,找不到才附加到頁尾。
|
||||
> 參數語意:`<key-col>` 的 `2` 只在舊頁還是表格時用得到,代表轉檔時取第 2 欄格子的文字當 H2 標題,格子是 `[文字](網址)` 就只取文字;頁面已經是條列格式就完全忽略它。`<key>` 是 H2 標題文字,也就是內容頁頁名 `LEARN_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。
|
||||
> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}` 決定,換主機名、改存取庫、頁名編碼有差都動不到它;連結帶著主機名與存取庫名,一變就比不到鍵,同一個存取庫會多出第二個區塊。
|
||||
> 禁止整頁覆蓋,也不得改動別人的區塊。
|
||||
|
||||
| 存取庫名稱 | 教訓紀錄 | 最後更新時間 |
|
||||
| --- | --- | --- |
|
||||
| {owner}/{repo} | [[LEARN_{HASH}]] | {yyyy-MM-dd HH:mm} |
|
||||
## LEARN_{HASH}
|
||||
|
||||
- 存取庫名稱:{owner}/{repo}
|
||||
- 教訓紀錄:[LEARN_{HASH}]({教訓頁絕對網址})
|
||||
- 最後更新時間:{yyyy-MM-dd HH:mm}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
# 日誌目錄
|
||||
|
||||
> 由 `jsc-log:worklog` 維護。每個日誌頁一列;頁名使用共享 `HASH` 規則,頁內仍依該週五日期整理。
|
||||
> 寫入語意:一列代表一個日誌頁。先讀整頁,找得到該頁既有的那一列就更新那一列,找不到才新增一列。
|
||||
> 禁止整頁覆蓋,也不得改動別人的列。
|
||||
> 由 `jsc-log:worklog` 維護。每個日誌頁一個區塊;`{HASH}` 是 `{owner}/{repo}` 的完整 40 碼大寫十六進位 SHA-1,頁內仍依該週五日期整理。
|
||||
> 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,日誌頁 `LOG_{HASH}` 落在 `JSC_WIKI_REPO_LOG` 的存取庫,兩者分屬不同 wiki。
|
||||
> 連結寫法:日誌頁那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。
|
||||
> 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。
|
||||
> 寫入語意:一個區塊代表一個日誌頁。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LOG 1 LOG_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該頁既有的那個區塊就換掉,找不到才附加到頁尾。
|
||||
> 參數語意:`<key-col>` 的 `1` 只在舊頁還是表格時用得到,代表轉檔時取第 1 欄格子的文字當 H2 標題;頁面已經是條列格式就完全忽略它。`<key>` 是 H2 標題文字,也就是內容頁頁名 `LOG_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。
|
||||
> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}` 決定:`GITEA_HOST` 一換、`JSC_WIKI_REPO_LOG` 改指別的存取庫,或 Gitea 對頁名的編碼有差,都動不到它。連結帶著主機名與存取庫名,這三件事任一變動就跟上一輪寫的不一樣;拿連結當鍵就比不到既有那一筆,走附加,同一個日誌頁多出第二個區塊,舊區塊從此不再更新。
|
||||
> 禁止整頁覆蓋,也不得改動別人的區塊。
|
||||
|
||||
| 日誌頁 | 週五日期 | 條目數 | 最後更新 |
|
||||
| --- | --- | --- | --- |
|
||||
| [[LOG_{HASH}]] | {yyyy-MM-dd} | {n} | {yyyy-MM-dd HH:mm} |
|
||||
## LOG_{HASH}
|
||||
|
||||
- 日誌頁:[LOG_{HASH}]({日誌頁絕對網址})
|
||||
- HASH:{HASH}
|
||||
- 週五日期:{yyyy-MM-dd}
|
||||
- 條目數:{n}
|
||||
- 最後更新:{yyyy-MM-dd HH:mm}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
> 任務有三種:一個工作包、一輪 PR 留言修正、一個獨立的修正提交。同一個工作包跑五輪留言修正就是五筆,
|
||||
> 標題各自寫清楚是哪一輪,時間與 token 分開記。
|
||||
> `HASH` 依共享規則計算;頁名不再寫入年月週數,但頁內仍依本工作週的週五整理內容。
|
||||
> {PLAN 頁絕對網址}、{ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得——LOG 與 PLAN/ANALYZE 可能分屬不同存取庫,`[[頁名]]` 跨庫不通。
|
||||
> 連結寫法:計畫名稱、工作包編號、PR 目標分支三欄一律寫成 `[{文字}]({絕對網址})`,wiki 頁的網址由 `jsc-gitea/tools/gitea.sh wiki-url` 取得,不用 `[[...]]`。LOG 與 PLAN/ANALYZE 可能分屬不同存取庫,`[[...]]` 跨庫連不過去,畫面上還看不出壞掉。
|
||||
> 連結驗證:條目裡的每個連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。
|
||||
> 解不出 wiki 存取庫或頁面不存在時,該欄填「無」,其他欄照填。由 `maintain` 觸發的日誌本來就沒有計畫頁與分析頁。
|
||||
|
||||
---
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
# 報表目錄
|
||||
|
||||
> 由 `jsc-log:report` 維護。年、月、週、日各一頁;`HASH` 取 `{owner}/{repo}/{期間}`,算法與其他頁面共用。
|
||||
> 寫入語意:一列代表一個報表頁,也就是一個存取庫的一種期間。先讀整頁,找得到該報表頁既有的那一列就更新那一列,找不到才新增一列。
|
||||
> 禁止整頁覆蓋,也不得改動別人的列。
|
||||
> 由 `jsc-log:report` 維護。年、月、週、日各一個區塊;`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼大寫十六進位 SHA-1,算法與其他頁面共用,但這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫。
|
||||
> 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,報表頁 `REPORT_{HASH}` 落在 `JSC_WIKI_REPO_REPORT` 的存取庫,兩者分屬不同 wiki。
|
||||
> 連結寫法:報表頁那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。
|
||||
> 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。
|
||||
> 寫入語意:一個區塊代表一個報表頁,也就是一個存取庫的一種期間。一律用 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 REPORT_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該報表頁既有的那個區塊就換掉,找不到才附加到頁尾。
|
||||
> 參數語意:`<key-col>` 的 `1` 只在舊頁還是表格時用得到,代表轉檔時取第 1 欄格子的文字當 H2 標題,格子是 `[文字](網址)` 就只取文字;頁面已經是條列格式就完全忽略它。`<key>` 是 H2 標題文字,也就是內容頁頁名 `REPORT_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。
|
||||
> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}/{期間}` 決定,`GITEA_HOST` 一換或 Gitea 對頁名的編碼有差都動不到它;連結帶著主機名與頁名編碼,一變就跟上一輪寫的不一樣,拿它當鍵就比不到既有那一筆,走附加,同一個報表頁多出第二個區塊,舊區塊從此不再更新。
|
||||
> 但 `JSC_WIKI_REPO_REPORT` 改指別的存取庫是另一回事:`HASH` 本身就取自 REPORT wiki 存取庫,換庫等於換頁,四個期間會各多一個區塊。那是換庫的本意,不是鍵失準,舊區塊請人工清掉。
|
||||
> 禁止整頁覆蓋,也不得改動別人的區塊。
|
||||
|
||||
| 報表頁 | 期間 | 最新一期 | 期數 | 最後更新 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| [[REPORT_{HASH}]] | {daily、weekly、monthly、yearly 四選一} | {最新一期的標籤} | {n} | {yyyy-MM-dd HH:mm} |
|
||||
## REPORT_{HASH}
|
||||
|
||||
- 報表頁:[REPORT_{HASH}]({報表頁絕對網址})
|
||||
- HASH:{HASH}
|
||||
- 期間:{daily、weekly、monthly、yearly 四選一}
|
||||
- 最新一期:{最新一期的標籤}
|
||||
- 期數:{n}
|
||||
- 最後更新:{yyyy-MM-dd HH:mm}
|
||||
|
||||
+131
-32
@@ -21,14 +21,30 @@
|
||||
# worklog-pending.sh commit <hash> <claim> 寫入成功後清掉併入清單上的暫存檔
|
||||
# worklog-pending.sh abort <hash> <claim> 寫入失敗後保留暫存,只丟掉合併檔與併入清單
|
||||
#
|
||||
# <hash> 為 jsc-gitea/tools/hash-id 算出的 8 碼工作日誌 hash,也就是 LOG_{HASH} 的 HASH。
|
||||
# <hash> 為 jsc-gitea/tools/hash-id 算出的工作日誌 hash,也就是 LOG_{HASH} 的 HASH。
|
||||
# 現行是完整 40 碼大寫十六進位;8 碼那種是舊規則留下的暫存檔,還沒遷移完之前一併收。
|
||||
# 舊規則的首碼落在 0-9ABC 就改寫成 H 加原前 7 碼,十六個首碼有十三個會命中,所以既有的
|
||||
# 8 碼暫存大多長成 H1A2B3C4。不收這種,遷移期間存進去的內容就再也取不回來。
|
||||
# 存放位置: $JSC_HOME/worklog-pending/{hash}/{UTC 時間}-{pid}.md(JSC_HOME 預設 ~/.jsc)
|
||||
# 合併檔: $JSC_HOME/worklog-pending/.merge/{hash}-{UTC 時間}-{pid}.md
|
||||
# 併入清單: 同名換副檔名 .claim,一行一個被併走的暫存檔路徑
|
||||
#
|
||||
# 結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=該 hash 沒有暫存內容
|
||||
# 結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=該 hash 沒有暫存內容 4=orphans 掃到孤兒目錄
|
||||
# merge 沒有暫存內容時仍然 exit 0:合併檔至少有本次條目,呼叫端不必分兩條路走。
|
||||
#
|
||||
# 舊目錄自動收編:
|
||||
# 拿 40 碼來查時,一併收「H 加前 7 碼」那個舊目錄。雜湊規則從 8 碼改成 40 碼那一刻,
|
||||
# 還沒寫進 wiki 的暫存會留在舊名底下;不收的話它查不到,而且沒有人會說查不到——
|
||||
# 「沒有暫存」與「暫存卡在舊名底下」對呼叫端長得一模一樣,兩種都是成功。日誌就這樣消失。
|
||||
# 只認「舊名等於 H 加新名前 7 碼」這一種對映,推導唯一,不會收到別的專案。
|
||||
# list、cat、merge 讀兩個目錄,clear 清兩個,commit 也放行舊目錄的路徑——
|
||||
# 放行漏掉的話,已經寫進 wiki 的暫存清不掉,下一輪會整批重複寫一次。
|
||||
#
|
||||
# orphans 子命令(不吃 hash):
|
||||
# 掃暫存區根目錄,印出每一個不是 40 碼的目錄:{目錄名}<TAB>{檔數}<TAB>{路徑}。
|
||||
# 沒有就 exit 0,有就 exit 4。上面那條自動收編只救得到推得出對映的舊目錄,
|
||||
# 推不出來的要有人看到才處理得掉,所以另外給一支掃描。
|
||||
#
|
||||
# 陷阱:
|
||||
# - clear 與 commit 都只在日誌確實寫進 wiki 之後才呼叫。先清再寫,寫失敗就兩邊都沒有。
|
||||
# - commit 只認 merge 產出的併入清單,且清單上的路徑必須落在該 hash 的暫存目錄底下,
|
||||
@@ -55,11 +71,50 @@ EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
valid_hash() { # 只收 8 碼大寫英數,擋掉路徑穿越
|
||||
case "$1" in
|
||||
[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]) return 0 ;;
|
||||
*) echo "[jsc][工作日誌暫存][ERR]:hash 須為 8 碼大寫英數,收到「${1:-空值}」。" >&2; exit 2 ;;
|
||||
# hash 會直接拼進暫存目錄路徑,這裡是整支腳本唯一的格式驗證,擋的是路徑穿越。
|
||||
# 放寬長度不等於放寬字元集:先確認每一個字都是十六進位,再比對長度。字元集這一關先過,
|
||||
# 「.」與「/」才進不了路徑;只看長度就會讓 ../ 那類值溜進來。
|
||||
# 字元檢查逐字剝,不呼叫 grep 之類逐行比對的工具:值裡夾一個換行,逐行比對會拿第一行
|
||||
# 當整個值判過,那正是路徑穿越要的缺口。
|
||||
#
|
||||
# 這份樣式與 jsc-gitea/tools/page-name.sh、jsc-hooks/hooks/comment-scope.sh 是三份各自
|
||||
# 獨立的定義,故意不共用:hook 與這個暫存區都必須自足,執行期不能相依別的 plugin 的安裝
|
||||
# 路徑——那個路徑每個 CLI 不一樣,也可能根本沒裝,抓不到就等於整道護欄失效。三份的一致性
|
||||
# 改在稽核時比對。
|
||||
valid_hash() {
|
||||
raw="${1:-}"
|
||||
# H 開頭先剝掉再驗其餘:舊規則把首碼落在 0-9ABC 的 hash 改寫成 H 加原前 7 碼,
|
||||
# H 本身不是十六進位字元,不先剝就會被逐字剝那一關擋掉。剝的是固定字面 H,
|
||||
# 不是一個字元集,所以放進來的字仍然只有 H 與十六進位這兩種。
|
||||
case "$raw" in
|
||||
H*) rest="${raw#H}"; hprefixed=1 ;;
|
||||
*) rest="$raw"; hprefixed=0 ;;
|
||||
esac
|
||||
while :; do
|
||||
case "$rest" in
|
||||
[0-9A-F]*) rest="${rest#?}" ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
if [ -z "$rest" ]; then
|
||||
if [ "$hprefixed" -eq 1 ]; then
|
||||
case "$raw" in
|
||||
????????) return 0 ;;
|
||||
esac
|
||||
else
|
||||
case "$raw" in
|
||||
????????????????????????????????????????) return 0 ;;
|
||||
????????) return 0 ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
echo "[jsc][工作日誌暫存][ERR]:hash 須為 40 碼大寫十六進位(現行),或 8 碼大寫十六進位、H 加 7 碼大寫十六進位(尚未遷移的舊暫存),收到「${1:-空值}」。" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
claim_reject() { # 併入清單上出現不該清的路徑,一律當用法錯誤擋下
|
||||
echo "[jsc][工作日誌暫存][ERR]:併入清單裡的「$1」不在這個 hash 的暫存目錄底下,不清。" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
valid_claim() { # 併入清單只認 merge 產出的那一份,擋掉拿別處的清單來刪檔
|
||||
@@ -72,10 +127,55 @@ valid_claim() { # 併入清單只認 merge 產出的那一份,擋掉拿別處
|
||||
|
||||
cmd="${1:-}"; [ -n "$cmd" ] || usage
|
||||
shift || true
|
||||
|
||||
# orphans 不吃 hash:它要掃的就是「算不出對應 hash」的那些目錄。
|
||||
if [ "$cmd" = orphans ]; then
|
||||
[ -d "$ROOT" ] || exit 0
|
||||
found=0
|
||||
for d in "$ROOT"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
name=$(basename "$d")
|
||||
[ "$name" = .merge ] && continue
|
||||
case "$name" in
|
||||
????????????????????????????????????????) continue ;;
|
||||
esac
|
||||
n=0
|
||||
for f in "$d"*.md; do [ -f "$f" ] && n=$((n + 1)); done
|
||||
printf '%s\t%s\t%s\n' "$name" "$n" "$d"
|
||||
found=1
|
||||
done
|
||||
[ "$found" -eq 0 ] && exit 0
|
||||
echo "[jsc][工作日誌暫存][ERR]:上列暫存目錄不是現行的 40 碼頁名,現行程式查不到它們,裡面的日誌永遠寫不進 wiki。舊名是新名的「H 加前 7 碼」,把目錄改成完整 40 碼即可。" >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
hash="${1:-}"; [ -n "$hash" ] || usage
|
||||
valid_hash "$hash"
|
||||
dir="$ROOT/$hash"
|
||||
|
||||
# 舊規則的同一個專案會落在「H 加前 7 碼」那個目錄。拿 40 碼來查時一併收,否則規則改版
|
||||
# 那一刻還沒寫進 wiki 的暫存就成為孤兒——查不到、也沒有人會說查不到,日誌就這樣消失。
|
||||
# 只認這一種對映:舊名完全等於 H 加新名前 7 碼,推導唯一,不會收錯別的專案。
|
||||
legacy_dir=""
|
||||
case "$hash" in
|
||||
????????????????????????????????????????)
|
||||
_cand="$ROOT/H$(printf '%s' "$hash" | cut -c1-7)"
|
||||
[ -d "$_cand" ] && legacy_dir="$_cand" ;;
|
||||
esac
|
||||
|
||||
# 兩個目錄的暫存檔一起排。檔名開頭是 UTC 時間戳,字典序等同時間序,所以跨目錄排完
|
||||
# 仍然是「舊的在前」。分開接的話,舊目錄那幾筆會整批插到新目錄最舊那一筆前面。
|
||||
pending_files() {
|
||||
for _d in "$dir" "$legacy_dir"; do
|
||||
[ -n "$_d" ] && [ -d "$_d" ] || continue
|
||||
for _f in "$_d"/*.md; do
|
||||
[ -f "$_f" ] || continue
|
||||
# 先印檔名再印全路徑,排完再把檔名那一欄切掉:排序的鍵是檔名,不是目錄。
|
||||
printf '%s\t%s\n' "$(basename "$_f")" "$_f"
|
||||
done
|
||||
done | sort | cut -f2-
|
||||
}
|
||||
|
||||
case "$cmd" in
|
||||
add)
|
||||
src="${2:-}"
|
||||
@@ -88,30 +188,26 @@ case "$cmd" in
|
||||
printf '%s\n' "$target"
|
||||
;;
|
||||
list)
|
||||
[ -d "$dir" ] || exit 3
|
||||
found=0
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
printf '%s\n' "$f"
|
||||
found=1
|
||||
done
|
||||
pending_files | while IFS= read -r f; do printf '%s\n' "$f"; done
|
||||
pending_files | grep -q . && found=1
|
||||
[ "$found" -eq 1 ] || exit 3
|
||||
;;
|
||||
cat)
|
||||
[ -d "$dir" ] || exit 3
|
||||
found=0
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
cat "$f"
|
||||
printf '\n'
|
||||
found=1
|
||||
done
|
||||
pending_files | while IFS= read -r f; do cat "$f"; printf '\n'; done
|
||||
pending_files | grep -q . && found=1
|
||||
[ "$found" -eq 1 ] || exit 3
|
||||
;;
|
||||
clear)
|
||||
[ -d "$dir" ] || exit 3
|
||||
rm -rf "$dir" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$dir」。" >&2; exit 1; }
|
||||
printf '已清除暫存:%s\n' "$dir"
|
||||
cleared=0
|
||||
for d in "$dir" "$legacy_dir"; do
|
||||
[ -n "$d" ] && [ -d "$d" ] || continue
|
||||
rm -rf "$d" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$d」。" >&2; exit 1; }
|
||||
printf '已清除暫存:%s\n' "$d"
|
||||
cleared=1
|
||||
done
|
||||
[ "$cleared" -eq 1 ] || exit 3
|
||||
;;
|
||||
merge)
|
||||
# 合成這一次要寫進 wiki 的內容:暫存的在前(依時間),本次條目在後。
|
||||
@@ -126,15 +222,13 @@ case "$cmd" in
|
||||
: > "$merged" || { echo "[jsc][工作日誌暫存][ERR]:寫不進「$merged」。" >&2; exit 1; }
|
||||
: > "$claim" || { echo "[jsc][工作日誌暫存][ERR]:寫不進「$claim」。" >&2; exit 1; }
|
||||
taken=0
|
||||
if [ -d "$dir" ]; then
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
cat "$f" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:讀不到「$f」。" >&2; exit 1; }
|
||||
printf '\n' >> "$merged"
|
||||
printf '%s\n' "$f" >> "$claim"
|
||||
taken=$((taken + 1))
|
||||
done
|
||||
fi
|
||||
pending_files > "$claim" || { echo "[jsc][工作日誌暫存][ERR]:列不出暫存檔。" >&2; exit 1; }
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
cat "$f" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:讀不到「$f」。" >&2; exit 1; }
|
||||
printf '\n' >> "$merged"
|
||||
taken=$((taken + 1))
|
||||
done < "$claim"
|
||||
cat "$src" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:併不進本次條目「$src」。" >&2; exit 1; }
|
||||
printf '\n' >> "$merged"
|
||||
printf 'MERGED=%s\n' "$merged"
|
||||
@@ -150,15 +244,20 @@ case "$cmd" in
|
||||
cleared=0
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
# 舊目錄也要放行:merge 會把「H 加前 7 碼」那個舊目錄的暫存一起併走,
|
||||
# 這裡不認的話,寫進 wiki 的內容清不掉,下一輪會整批重複寫一次。
|
||||
case "$f" in
|
||||
"$dir"/*) ;;
|
||||
*) echo "[jsc][工作日誌暫存][ERR]:併入清單裡的「$f」不在「$dir」底下,不清。" >&2; exit 2 ;;
|
||||
*) if [ -n "$legacy_dir" ]; then
|
||||
case "$f" in "$legacy_dir"/*) ;; *) claim_reject "$f" ;; esac
|
||||
else claim_reject "$f"; fi ;;
|
||||
esac
|
||||
[ -e "$f" ] || continue
|
||||
rm -f "$f" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$f」。" >&2; exit 1; }
|
||||
cleared=$((cleared + 1))
|
||||
done < "$claim"
|
||||
rmdir "$dir" 2>/dev/null || true
|
||||
[ -n "$legacy_dir" ] && rmdir "$legacy_dir" 2>/dev/null || true
|
||||
rm -f "$claim" "${claim%.claim}.md"
|
||||
printf '已清除暫存 %s 個檔案(wiki 寫入成功後才做)。\n' "$cleared"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user