release: v0.1.1 develop 到 master #18
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||||
"skills": "./skills"
|
"skills": "./skills"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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/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) |
|
| `tools/worklog-target.sh` | 接收已由 `jsc-gitea/tools/hash-id` 算好的 `HASH`,組出 `LOG_{HASH}`、`LOG_CONTENTS`(本身不再計算 SHA-1) |
|
||||||
| `tools/worklog-pending.sh` | 待寫入日誌的暫存區,存放於 `$JSC_HOME/worklog-pending/{HASH}/`。`add {HASH} {檔案}` 存一段內容(`jsc-sdlc` 的階段回報發現沒寫日誌時會呼叫),`cat {HASH}` 依時間印出全部、`list` 列路徑、`clear` 清除。結束碼 `3` 代表沒有暫存內容。**寫進 wiki 成功之後才 clear**,先清再寫會兩邊都沒有 |
|
| `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/report-range.sh` | 算報表期間:`report-range.sh {daily\|weekly\|monthly\|yearly} [yyyy-MM-dd]` 印出「起<TAB>訖<TAB>標籤<TAB>期間」,含頭含尾。週採 ISO-8601(週一起算),標籤如 `2026-W35`。日期運算交給系統的 `date`,不自己算閏年 |
|
| `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/report-template.sh` | 解析報表範本位置:`resolve {period}` 印出「路徑<TAB>project\|skill」,`list` 一次列四種期間。工作目錄的 `.jsc/templates/report-{period}.md` 優先於技能自帶的 `templates/` |
|
||||||
| `tools/token-usage.sh` | 讀單一 CLI 這次工作的 token 用量,印出「input(tab)output」;來源讀不到就印「N/A(tab)N/A」並正常結束。第二個參數傳 session id,就只讀該階段的 transcript,數字才會跟花費時間對得上。各 CLI 的取得方式寫在腳本開頭註解 |
|
| `tools/token-usage.sh` | 讀單一 CLI 這次工作的 token 用量,印出「input(tab)output」;來源讀不到就印「N/A(tab)N/A」並正常結束。第二個參數傳 session id,就只讀該階段的 transcript,數字才會跟花費時間對得上。各 CLI 的取得方式寫在腳本開頭註解 |
|
||||||
@@ -37,7 +37,9 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
### `worklog`
|
### `worklog`
|
||||||
|
|
||||||
工作完成後蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支),用 `tools/worklog-target.sh` 產生目標頁,再套範本附加到 `LOG_{HASH}` 與 `LOG_CONTENTS`。寫入前先讀 `tools/worklog-pending.sh cat {HASH}`:之前有階段跑完沒寫日誌,內容會暫存在那裡,這次一併寫進去,**寫成功之後才清暫存**。
|
每完成一個任務就寫一筆日誌。任務有三種:一個工作包、一輪 PR 留言修正、一個獨立的修正提交。下一個任務開始前先把這一筆寫完,同一個工作包跑五輪留言修正就是五筆,各自帶自己的花費時間與 token 用量,附加到同一頁 `LOG_{HASH}`——連「試了卻沒改到檔案」的那一輪也留下來,那段時間才看得見。
|
||||||
|
|
||||||
|
每筆蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支),用 `tools/worklog-target.sh` 產生目標頁,套範本後附加到 `LOG_{HASH}` 與 `LOG_CONTENTS`。寫入前跑 `tools/worklog-pending.sh merge {HASH} {本次條目檔}`:之前有階段跑完沒寫日誌,內容暫存在那裡,這次一併寫進去;wiki 寫入成功才 `commit` 清掉暫存,失敗就 `abort` 保留。
|
||||||
|
|
||||||
### `stats`
|
### `stats`
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
"description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)",
|
||||||
"skills": "./skills/"
|
"skills": "./skills/"
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-13
@@ -1,11 +1,23 @@
|
|||||||
---
|
---
|
||||||
name: worklog
|
name: worklog
|
||||||
description: After finishing a work package, collect ten facts (repo, branch, plan link, work package link, elapsed time from session-timer, token usage per CLI, status, details, difficulties, PR target) and append a templated entry to wiki LOG_{HASH} plus LOG_CONTENTS. HASH follows the shared 8-char rule with the H-prefix fallback, and the work-week Friday still drives the page content. Merge whatever tools/worklog-pending.sh holds for that HASH into the same write, then clear the pending area once the wiki write succeeded. Trigger at the end of 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 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.
|
||||||
---
|
---
|
||||||
|
|
||||||
# worklog — work log
|
# worklog — work log
|
||||||
|
|
||||||
After work completes, collect the ten items below and append a `templates/log-entry.md` entry to the wiki log page. Collection and writing MUST run as a sub agent. The entry content is written in Traditional Chinese (STE100).
|
Collection and writing MUST run as a sub agent. Entry content is written in Traditional Chinese (STE100).
|
||||||
|
|
||||||
|
## What counts as one task
|
||||||
|
|
||||||
|
| Task | Ends when |
|
||||||
|
| --- | --- |
|
||||||
|
| A work package | its todos are done and its PR is open |
|
||||||
|
| One round of PR-comment fixes | that round's replies and pushes are done |
|
||||||
|
| A standalone fix commit | that commit is pushed |
|
||||||
|
|
||||||
|
Write the entry for a task before the next task starts — that is the whole granularity rule. Five rounds of comment fixes on one work package produce five entries on the same `LOG_{HASH}`, each with its own elapsed time and token count, including a round that tried something and changed no file: the time it burned is the fact worth keeping. Give each entry a heading that names the task (work package id, round number, or commit subject) so the five stay readable side by side.
|
||||||
|
|
||||||
|
Guardrail: append entries at the end of the page and leave the existing ones untouched.
|
||||||
|
|
||||||
## Items to collect
|
## Items to collect
|
||||||
|
|
||||||
@@ -14,23 +26,24 @@ After work completes, collect the ten items below and append a `templates/log-en
|
|||||||
| 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 |
|
| 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` |
|
| 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 |
|
| 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 |
|
||||||
| 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}`. Same fallback as row 3: `wiki-repo` exit 3 or `wiki-url` exit 4 → fill 「無」 and carry on |
|
| 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 fallback as row 3: `wiki-repo` exit 3 or `wiki-url` exit 4 → fill 「無」 and carry on |
|
||||||
| 5 | Elapsed time | `jsc-hooks/hooks/session-timer.sh report {session_id}` (seconds; convert to h/m) |
|
| 5 | Elapsed time | `jsc-hooks/hooks/session-timer.sh report {session_id}` (seconds; convert to h/m). Count only this task, so read it at the moment the task ends |
|
||||||
| 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 session. 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 |
|
| 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 work package, 「部分完成」 leaves the remainder open for the next run, 「阻塞」 records the blocker and hands it back to the operator) |
|
| 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 |
|
| 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 |
|
| 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 |
|
||||||
|
|
||||||
The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`.
|
The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`.
|
||||||
|
|
||||||
## Target page and work week
|
## Write the entry
|
||||||
|
|
||||||
1. Resolve the wiki repo hosting LOG pages: `JSC_WIKI_REPO_LOG` first, then `JSC_WIKI_REPO`, via `gitea.sh wiki-repo LOG`. Inspect the inherited shell environment variables first; ask the user per the `jsc-ask:ask` rules only when neither resolves. Never borrow another type's `JSC_WIKI_REPO_{TYPE}`. Done when the hosting `{owner}/{repo}` is known.
|
1. Resolve the wiki repo hosting LOG pages with `gitea.sh wiki-repo LOG`: it reads `JSC_WIKI_REPO_LOG` first and falls back to `JSC_WIKI_REPO` only when that one is unset. Inspect the inherited shell environment first, and ask the user per the `jsc-ask:ask` rules when neither resolves. Keep to the LOG variable — another page type's `JSC_WIKI_REPO_{TYPE}` never stands in for it. Done when the hosting `{owner}/{repo}` is known.
|
||||||
2. Compute `{HASH}` from the code repo's `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. Done when the 8-character `{HASH}` is known.
|
2. Compute `{HASH}` from the code repo's `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. Done when the 8-character `{HASH}` is known.
|
||||||
3. Run `tools/worklog-target.sh "{HASH}" all`. Use `PAGE` for `LOG_{HASH}` and `CONTENTS` for `LOG_CONTENTS`. Done when both page names are known.
|
3. Run `tools/worklog-target.sh "{HASH}" all`. Use `PAGE` for `LOG_{HASH}` and `CONTENTS` for `LOG_CONTENTS`. Done when both page names are known.
|
||||||
4. Fix the work week: the Friday of the current work week drives the page content and the row dates. Done when that Friday is fixed as a `yyyy-MM-dd` date.
|
4. Fix the work week: the Friday of the current work week drives the page content and the row dates. Done when that Friday is fixed as a `yyyy-MM-dd` date.
|
||||||
5. Collect what the pending area holds for this `{HASH}`: run `tools/worklog-pending.sh cat {HASH}`. Exit 3 means nothing is pending — carry on with this run's entry alone. Anything it prints was written by an earlier SDLC stage that ended without a work log, so it goes into **this** write, ahead of this run's own entry, in the order printed. Done when the pending content is either merged into the entries about to be written, or confirmed empty.
|
5. 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.
|
||||||
6. Read `PAGE` via `jsc-gitea:wiki`. If it does not exist, create it with the structure described in `templates/log-entry.md`; otherwise APPEND the new entries at the end and never overwrite existing entries. Done when every entry from step 5 plus this run's own entry exists on `PAGE`.
|
6. 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. Done when `MERGED` and `CLAIM` are known.
|
||||||
7. Update `CONTENTS` in the same pass (apply `templates/log-contents.md`; add the row if missing, otherwise refresh its 條目數 and 最後更新). Done when the row for `PAGE` carries this week's Friday date.
|
7. Read `PAGE` via `jsc-gitea:wiki`. Create it from the structure in `templates/log-entry.md` when it does not exist, then append the whole `MERGED` content at the end. Done when every entry in `MERGED` exists on `PAGE`.
|
||||||
8. Clear the pending area: run `tools/worklog-pending.sh clear {HASH}` **only after the wiki write of step 6 succeeded**. Clearing first and failing the write loses the content on both sides. Skip this when step 5 found nothing. Done when the script reports the cleared path, or step 5 was empty.
|
8. Update `CONTENTS` in the same pass (apply `templates/log-contents.md`; add the row if missing, otherwise refresh its 條目數 and 最後更新). Done when the row for `PAGE` carries this week's Friday date.
|
||||||
|
9. Close the pending area on the result of steps 7 and 8: `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 6. Done when one of the two ran and printed its count.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# 工作日誌頁 — LOG_{HASH}
|
# 工作日誌頁 — LOG_{HASH}
|
||||||
|
|
||||||
> 由 `jsc-log:worklog` 維護。每完成一項工作附加一個條目在文末。
|
> 由 `jsc-log:worklog` 維護。每完成一個任務就附加一個條目在文末,下一個任務開始前寫完。
|
||||||
|
> 任務有三種:一個工作包、一輪 PR 留言修正、一個獨立的修正提交。同一個工作包跑五輪留言修正就是五筆,
|
||||||
|
> 標題各自寫清楚是哪一輪,時間與 token 分開記。
|
||||||
> `HASH` 依共享規則計算;頁名不再寫入年月週數,但頁內仍依本工作週的週五整理內容。
|
> `HASH` 依共享規則計算;頁名不再寫入年月週數,但頁內仍依本工作週的週五整理內容。
|
||||||
> {PLAN 頁絕對網址}、{ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得——LOG 與 PLAN/ANALYZE 可能分屬不同存取庫,`[[頁名]]` 跨庫不通。
|
> {PLAN 頁絕對網址}、{ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得——LOG 與 PLAN/ANALYZE 可能分屬不同存取庫,`[[頁名]]` 跨庫不通。
|
||||||
> 解不出 wiki 存取庫或頁面不存在時,該欄填「無」,其他欄照填。由 `maintain` 觸發的日誌本來就沒有計畫頁與分析頁。
|
> 解不出 wiki 存取庫或頁面不存在時,該欄填「無」,其他欄照填。由 `maintain` 觸發的日誌本來就沒有計畫頁與分析頁。
|
||||||
|
|||||||
@@ -6,32 +6,50 @@
|
|||||||
# 階段回報發現沒有日誌,就把該階段的內容存進本區並警告使用者;下次 jsc-log:worklog 寫入時
|
# 階段回報發現沒有日誌,就把該階段的內容存進本區並警告使用者;下次 jsc-log:worklog 寫入時
|
||||||
# 先把本區的內容一併寫進去,寫完才清掉。
|
# 先把本區的內容一併寫進去,寫完才清掉。
|
||||||
#
|
#
|
||||||
|
# 一次寫入的三段式(merge、commit、abort)解決同一件事:暫存只能在 wiki 寫入成功之後才清。
|
||||||
|
# merge 把暫存內容與本次條目合成一份要寫進 wiki 的檔案,同時記下這次併走了哪些暫存檔(併入清單)。
|
||||||
|
# commit wiki 寫入成功後呼叫,只刪掉併入清單上那幾個檔。
|
||||||
|
# abort wiki 寫入失敗後呼叫,暫存原封不動,只丟掉合併檔與併入清單。
|
||||||
|
# 只刪清單上的檔案,是為了保住 merge 之後、commit 之前另一個工作階段新存進來的內容。
|
||||||
|
#
|
||||||
# 用法:
|
# 用法:
|
||||||
# worklog-pending.sh add <hash> <file> 把一段待寫入的日誌內容存起來,印出存放路徑
|
# worklog-pending.sh add <hash> <file> 把一段待寫入的日誌內容存起來,印出存放路徑
|
||||||
# worklog-pending.sh list <hash> 列出該 hash 的暫存檔路徑(一行一個,依時間排序)
|
# worklog-pending.sh list <hash> 列出該 hash 的暫存檔路徑(一行一個,依時間排序)
|
||||||
# worklog-pending.sh cat <hash> 依時間順序印出全部暫存內容
|
# worklog-pending.sh cat <hash> 依時間順序印出全部暫存內容
|
||||||
# worklog-pending.sh clear <hash> 清掉該 hash 的暫存(寫入日誌成功後才做)
|
# worklog-pending.sh clear <hash> 清掉該 hash 的全部暫存(寫入日誌成功後才做)
|
||||||
|
# worklog-pending.sh merge <hash> <file> 合成「暫存內容+本次條目」,印出 MERGED= 與 CLAIM=
|
||||||
|
# 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 算出的 8 碼工作日誌 hash,也就是 LOG_{HASH} 的 HASH。
|
||||||
# 存放位置: $JSC_HOME/worklog-pending/{hash}/{UTC 時間}-{pid}.md(JSC_HOME 預設 ~/.jsc)
|
# 存放位置: $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 沒有暫存內容
|
||||||
|
# merge 沒有暫存內容時仍然 exit 0:合併檔至少有本次條目,呼叫端不必分兩條路走。
|
||||||
#
|
#
|
||||||
# 陷阱:
|
# 陷阱:
|
||||||
# - clear 只在日誌確實寫進 wiki 之後才呼叫。先清再寫,寫失敗就兩邊都沒有。
|
# - clear 與 commit 都只在日誌確實寫進 wiki 之後才呼叫。先清再寫,寫失敗就兩邊都沒有。
|
||||||
|
# - commit 只認 merge 產出的併入清單,且清單上的路徑必須落在該 hash 的暫存目錄底下,
|
||||||
|
# 不然就當成用法錯誤擋下來——這道護欄擋的是拿別人的清單來刪檔。
|
||||||
# - 檔名帶 UTC 時間與 pid,同一秒內兩個工作階段各存各的,不會互相覆蓋。
|
# - 檔名帶 UTC 時間與 pid,同一秒內兩個工作階段各存各的,不會互相覆蓋。
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
||||||
ROOT="$JSC_HOME/worklog-pending"
|
ROOT="$JSC_HOME/worklog-pending"
|
||||||
|
MERGE_DIR="$ROOT/.merge"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat >&2 <<'EOF'
|
cat >&2 <<'EOF'
|
||||||
用法:
|
用法:
|
||||||
worklog-pending.sh add <hash> <file> 存一段待寫入的日誌內容
|
worklog-pending.sh add <hash> <file> 存一段待寫入的日誌內容
|
||||||
worklog-pending.sh list <hash> 列出暫存檔路徑
|
worklog-pending.sh list <hash> 列出暫存檔路徑
|
||||||
worklog-pending.sh cat <hash> 印出全部暫存內容
|
worklog-pending.sh cat <hash> 印出全部暫存內容
|
||||||
worklog-pending.sh clear <hash> 清掉暫存(寫入日誌成功後才做)
|
worklog-pending.sh clear <hash> 清掉全部暫存(寫入日誌成功後才做)
|
||||||
|
worklog-pending.sh merge <hash> <file> 合成「暫存內容+本次條目」,印出 MERGED= 與 CLAIM=
|
||||||
|
worklog-pending.sh commit <hash> <claim> 寫入成功後清掉併入清單上的暫存檔
|
||||||
|
worklog-pending.sh abort <hash> <claim> 寫入失敗後保留暫存,只丟掉合併檔與併入清單
|
||||||
結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=沒有暫存內容
|
結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=沒有暫存內容
|
||||||
EOF
|
EOF
|
||||||
exit 2
|
exit 2
|
||||||
@@ -44,6 +62,14 @@ valid_hash() { # 只收 8 碼大寫英數,擋掉路徑穿越
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid_claim() { # 併入清單只認 merge 產出的那一份,擋掉拿別處的清單來刪檔
|
||||||
|
case "$1" in
|
||||||
|
"$MERGE_DIR"/*.claim) ;;
|
||||||
|
*) echo "[jsc][工作日誌暫存][ERR]:併入清單須是 merge 產出的「$MERGE_DIR/*.claim」,收到「$1」。" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
[ -f "$1" ] || { echo "[jsc][工作日誌暫存][ERR]:找不到併入清單「$1」。" >&2; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
cmd="${1:-}"; [ -n "$cmd" ] || usage
|
cmd="${1:-}"; [ -n "$cmd" ] || usage
|
||||||
shift || true
|
shift || true
|
||||||
hash="${1:-}"; [ -n "$hash" ] || usage
|
hash="${1:-}"; [ -n "$hash" ] || usage
|
||||||
@@ -87,6 +113,70 @@ case "$cmd" in
|
|||||||
rm -rf "$dir" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$dir」。" >&2; exit 1; }
|
rm -rf "$dir" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$dir」。" >&2; exit 1; }
|
||||||
printf '已清除暫存:%s\n' "$dir"
|
printf '已清除暫存:%s\n' "$dir"
|
||||||
;;
|
;;
|
||||||
|
merge)
|
||||||
|
# 合成這一次要寫進 wiki 的內容:暫存的在前(依時間),本次條目在後。
|
||||||
|
# 這裡只讀不刪——刪檔一律等 commit,也就是等 wiki 寫入回報成功。
|
||||||
|
src="${2:-}"
|
||||||
|
[ -n "$src" ] || usage
|
||||||
|
[ -f "$src" ] || { echo "[jsc][工作日誌暫存][ERR]:找不到本次條目檔「$src」。" >&2; exit 1; }
|
||||||
|
mkdir -p "$MERGE_DIR" || { echo "[jsc][工作日誌暫存][ERR]:建不出合併目錄「$MERGE_DIR」。" >&2; exit 1; }
|
||||||
|
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||||
|
merged="$MERGE_DIR/$hash-$stamp-$$.md"
|
||||||
|
claim="$MERGE_DIR/$hash-$stamp-$$.claim"
|
||||||
|
: > "$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
|
||||||
|
cat "$src" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:併不進本次條目「$src」。" >&2; exit 1; }
|
||||||
|
printf '\n' >> "$merged"
|
||||||
|
printf 'MERGED=%s\n' "$merged"
|
||||||
|
printf 'CLAIM=%s\n' "$claim"
|
||||||
|
printf 'PENDING=%s\n' "$taken"
|
||||||
|
;;
|
||||||
|
commit)
|
||||||
|
# 護欄:只有 wiki 寫入成功才輪到這一段。清單以外的暫存檔一律不動,
|
||||||
|
# 因為那是 merge 之後才存進來的內容,還沒被寫進任何一頁。
|
||||||
|
claim="${2:-}"
|
||||||
|
[ -n "$claim" ] || usage
|
||||||
|
valid_claim "$claim"
|
||||||
|
cleared=0
|
||||||
|
while IFS= read -r f; do
|
||||||
|
[ -n "$f" ] || continue
|
||||||
|
case "$f" in
|
||||||
|
"$dir"/*) ;;
|
||||||
|
*) echo "[jsc][工作日誌暫存][ERR]:併入清單裡的「$f」不在「$dir」底下,不清。" >&2; exit 2 ;;
|
||||||
|
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
|
||||||
|
rm -f "$claim" "${claim%.claim}.md"
|
||||||
|
printf '已清除暫存 %s 個檔案(wiki 寫入成功後才做)。\n' "$cleared"
|
||||||
|
;;
|
||||||
|
abort)
|
||||||
|
# wiki 寫入失敗時走這裡:暫存一個都不刪,下一次 merge 會再把它們併進去。
|
||||||
|
claim="${2:-}"
|
||||||
|
[ -n "$claim" ] || usage
|
||||||
|
valid_claim "$claim"
|
||||||
|
kept=0
|
||||||
|
while IFS= read -r f; do
|
||||||
|
[ -n "$f" ] || continue
|
||||||
|
if [ -e "$f" ]; then
|
||||||
|
kept=$((kept + 1))
|
||||||
|
fi
|
||||||
|
done < "$claim"
|
||||||
|
rm -f "$claim" "${claim%.claim}.md"
|
||||||
|
printf '暫存保留 %s 個檔案,合併檔與併入清單已丟掉。\n' "$kept"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage ;;
|
usage ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user