task-ledger 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-ledger",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Task-ledger skill for coding agents: per-workspace progress tracking (ledger/progress.json, history, worklog) driven by a CLI script. Standalone install for CodeBuddy and other agents; also bundled inside the dsh-task-ledger DSH plugin.",
5
5
  "keywords": [
6
6
  "skill",
@@ -447,6 +447,77 @@ def git_sync(action: str) -> None:
447
447
  )
448
448
 
449
449
 
450
+ # 只读命令不碰 git;其余(含写类与 tags)写前刷新 ledger/。
451
+ READ_ONLY_COMMANDS = {"list", "show", "history", "validate"}
452
+
453
+
454
+ def git_presync(action: str, args: argparse.Namespace) -> None:
455
+ """写命令执行前把 ledger/ 刷成远端最新,避免基于过期读取整文件回写。
456
+
457
+ 只动 ledger/ 目录(不碰项目其余文件),与 git_sync 共用 --no-sync / TASK_LEDGER_NO_SYNC。
458
+ 无 git / 无上游 / 远端尚无该路径 静默跳过;拉取失败只提示不中止(推送端 rebase 兜底)。
459
+ 本地未提交的 ledger 改动会被远端覆盖——正是「取最新再改」语义,仅打提示。
460
+ """
461
+ if os.environ.get("TASK_LEDGER_NO_SYNC"):
462
+ return
463
+ if action in READ_ONLY_COMMANDS:
464
+ return
465
+ # tags --all 是纯读,不拉取
466
+ if action == "tags" and not getattr(args, "rename", None):
467
+ return
468
+ try:
469
+ repo = find_repo(getattr(args, "repo", None))
470
+ except Fail:
471
+ return
472
+ ledger_dir = repo / "ledger"
473
+ top = _git("git", "-C", str(repo), "rev-parse", "--show-toplevel")
474
+ if top.returncode != 0:
475
+ return
476
+ git_root = Path(top.stdout.strip())
477
+ try:
478
+ ledger_rel = str(ledger_dir.resolve().relative_to(git_root.resolve()))
479
+ except ValueError:
480
+ return # ledger 不在 git 仓内
481
+ ups = _git("git", "-C", str(git_root), "rev-parse", "--abbrev-ref", "@{upstream}")
482
+ if ups.returncode != 0:
483
+ return # 无上游跟踪分支
484
+ upstream = ups.stdout.strip()
485
+ remote = upstream.split("/", 1)[0]
486
+ try:
487
+ _git("git", "-C", str(git_root), "fetch", remote, timeout=30, check=True)
488
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
489
+ print(
490
+ "提示:写前拉取失败(离线 / 无权限 / 超时),将基于本地当前内容写入;"
491
+ "若他人已改动,推送时可能冲突。",
492
+ file=sys.stderr,
493
+ )
494
+ return
495
+ if _git(
496
+ "git", "-C", str(git_root), "status", "--porcelain", ledger_rel
497
+ ).stdout.strip():
498
+ print(
499
+ "提示:本地 ledger/ 有未提交改动,写前拉取将用远端最新覆盖。",
500
+ file=sys.stderr,
501
+ )
502
+ # 只取远端 ledger/ 到工作树;该路径在远端不存在(全新台账)时忽略
503
+ try:
504
+ _git(
505
+ "git",
506
+ "-C",
507
+ str(git_root),
508
+ "checkout",
509
+ upstream,
510
+ "--",
511
+ ledger_rel,
512
+ timeout=30,
513
+ check=True,
514
+ )
515
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
516
+ pass
517
+ # 取消预暂存:只更新工作树,避免把他人改动混进本次提交(git_sync 只 add 本次改动的文件)
518
+ _git("git", "-C", str(git_root), "reset", "--", ledger_rel)
519
+
520
+
450
521
  def require_task_id(tid: str) -> str:
451
522
  """任务号会参与拼路径,任何命令拿到它都要先验一遍。"""
452
523
  if not ID_RE.fullmatch(tid):
@@ -2308,6 +2379,7 @@ def main(argv: list[str] | None = None) -> int:
2308
2379
  if getattr(args, "no_sync", False):
2309
2380
  os.environ["TASK_LEDGER_NO_SYNC"] = "1"
2310
2381
  try:
2382
+ git_presync(args.command, args) # 写前刷新 ledger/ 到远端最新
2311
2383
  args.func(args)
2312
2384
  except Fail as exc:
2313
2385
  print(f"错误:{exc}", file=sys.stderr)