task-ledger 1.3.8 → 1.3.9

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/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: task-ledger
3
3
  description: 本地任务台账,跨会话记录在办任务的归属、进度与起止。当用户要登记一件新的事(新建任务 / 建个坑 / 这活我领了)、记录已有任务的进展(接着做 / 记一条进展 / 这轮先记下)、查看任务状态或历史(现在该做什么 / 这活谁在做 / 上周做过什么)、给任务结案或退回(做完了 / 验收通过 / 不做了 / 退回待认领)、整理标签或检查台账一致性时加载;只是改代码、讨论方案时不要用。
4
- version: 1.3.8
4
+ version: 1.3.9
5
5
  allowed-tools: Read,Edit,Bash,Grep
6
6
  ---
7
7
 
@@ -45,7 +45,7 @@ allowed-tools: Read,Edit,Bash,Grep
45
45
  | `--claim <uuid>` | 认领名册里已有的 uuid(名字一起复制) |
46
46
  | `--new --name "…"` | 新建身份:生成 uuid + 名字 |
47
47
 
48
- 干什么:写本机身份文件(`~/.taskledger/identity.json`),**一台机器只一次**,与台账无关。在终端里**裸跑 `init` 即交互式**:读当前仓库名册——有候选就列出来让你**选编号认领**(或输 `n` 新建),名册为空就让你**输入用户名**;全程要真终端。`--claim <uuid>` / `--new --name "<名字>"` 也只接受真终端,agent 的无终端调用会被脚本拒绝。
48
+ 干什么:写本机身份文件(`~/.taskledger/identity.json`),**一台机器只一次**,与台账无关。在终端里**裸跑 `init` 即交互式选择框**:读当前仓库名册,用**上下箭头**移动、**Enter** 认领某个身份,末尾「+ 新建身份」可输入新用户名自建;名册为空则直接让你输入用户名;全程要真终端。`--claim <uuid>` / `--new --name "<名字>"` 也只接受真终端,agent 的无终端调用会被脚本拒绝。
49
49
 
50
50
  ### new — 建任务
51
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-ledger",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "description": "Task-ledger skill for coding agents: per-workspace progress tracking (ledger/pool.json + owners/<hash>.json shards + owners.json index, 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",
@@ -272,24 +272,99 @@ def _lock_dir(repo: Path) -> Path:
272
272
  def _print_candidates(opts: list[dict]) -> None:
273
273
  print("这台机器还没初始化。当前仓库里已有的用户:")
274
274
  for i, opt in enumerate(opts, 1):
275
- print(f" {i}. {opt['uuid']} {opt['name']}")
275
+ print(f" {i}. @{opt['name']} {opt['uuid']}")
276
276
 
277
277
 
278
278
  def _print_init_result(res: dict) -> None:
279
+ name_disp = res.get("name") or res["uuid"][:8]
279
280
  if res.get("already"):
280
- print(
281
- f"已经初始化过,未做改动:{res['name'] or res['uuid'][:8]}({res['uuid']})"
282
- )
281
+ print(f"已经初始化过,未做改动:@{name_disp} {res['uuid']}")
283
282
  print(f"文件:{res['path']}")
284
283
  return
285
- print(f"已初始化:{res['name'] or res['uuid'][:8]}({res['uuid']})")
284
+ print(f"\x1b[32m✓ 已初始化:\x1b[0m@{name_disp} {res['uuid']}")
286
285
  print(f"文件:{res['path']}")
287
286
 
288
287
 
288
+ def _select_identity(opts: list[dict], *, title: str) -> tuple[str, int | None]:
289
+ """交互选身份:箭头上下移动,末尾「+ 新建身份」,Enter 确认,Ctrl-C 取消。
290
+ 返回 (kind, index):kind∈{claim,new,cancel};claim/new 时 index 为候选项下标。
291
+ TTY 下走 raw 光标选择;不支持 raw(非 Unix / 无 TTY)时退回编号输入。仅在 TTY 调用。
292
+ """
293
+ entries = list(opts) + [{"new": True}]
294
+ n = len(entries)
295
+
296
+ try:
297
+ fd = sys.stdin.fileno()
298
+ if not sys.stdin.isatty():
299
+ raise OSError("not a tty")
300
+ import termios
301
+ import tty
302
+
303
+ old = termios.tcgetattr(fd)
304
+ sel = 0
305
+ try:
306
+ tty.setraw(fd)
307
+ while True:
308
+ sys.stdout.write("\x1b[H\x1b[J")
309
+ sys.stdout.write("\x1b[1;36m" + title + "\x1b[0m\n\n")
310
+ for i, e in enumerate(entries):
311
+ label = (
312
+ "+ 新建身份(自己输入名字)"
313
+ if e.get("new")
314
+ else f"@{e['name']} {e['uuid']}"
315
+ )
316
+ if i == sel:
317
+ sys.stdout.write(" \x1b[7m ❯ " + label + " \x1b[0m\n")
318
+ else:
319
+ sys.stdout.write(" " + label + "\n")
320
+ sys.stdout.write(
321
+ "\n \x1b[2m↑/↓ 移动 · Enter 确认 · Ctrl-C 退出\x1b[0m\n"
322
+ )
323
+ sys.stdout.flush()
324
+ ch = sys.stdin.read(1)
325
+ if ch == "\x1b":
326
+ nxt = sys.stdin.read(1)
327
+ if nxt in ("[", "O"):
328
+ k = sys.stdin.read(1)
329
+ if k == "A":
330
+ sel = (sel - 1) % n
331
+ elif k == "B":
332
+ sel = (sel + 1) % n
333
+ elif ch in ("\r", "\n"):
334
+ return ("new", None) if entries[sel].get("new") else ("claim", sel)
335
+ elif ch == "\x03": # Ctrl-C
336
+ return ("cancel", None)
337
+ finally:
338
+ termios.tcsetattr(fd, termios.TCSADRAIN, old)
339
+ sys.stdout.write("\x1b[2J\x1b[H")
340
+ sys.stdout.flush()
341
+ except (OSError, ImportError):
342
+ print(title)
343
+ for i, o in enumerate(opts, 1):
344
+ print(f" {i}. @{o['name']} {o['uuid']}")
345
+ print(f" {n}. + 新建身份")
346
+ while True:
347
+ try:
348
+ c = input(f"选编号(1-{n}):").strip()
349
+ except (EOFError, KeyboardInterrupt):
350
+ return ("cancel", None)
351
+ if c.lower() == "n":
352
+ return ("new", None)
353
+ try:
354
+ idx = int(c) - 1
355
+ except ValueError:
356
+ continue
357
+ if idx == len(opts):
358
+ return ("new", None)
359
+ if 0 <= idx < len(opts):
360
+ return ("claim", idx)
361
+ return ("cancel", None)
362
+
363
+
289
364
  def _run_init(args: argparse.Namespace) -> int:
290
365
  """init 不进 runner、不解析台账、不碰 git:只写机器身份文件。
291
366
 
292
- 交互式:裸 init 在真终端读名册、问用户选 / 输名字;无终端(agent)只报候选、不写。
367
+ 交互式:裸 init 在真终端读名册、让用户箭头选 / 新建;无终端(agent)只报候选、不写。
293
368
  """
294
369
  from .. import env
295
370
  from ..actions.init_identity import init_identity
@@ -330,14 +405,21 @@ def _run_init(args: argparse.Namespace) -> int:
330
405
  "请重跑:init --claim=<uuid> 认领,或 init --new --name=<名字> 新建"
331
406
  )
332
407
  return 0
333
- _print_candidates(opts)
334
- choice = input("选编号认领,或输入 n 新建:").strip()
335
- if choice.lower() == "n":
408
+ kind, idx = _select_identity(
409
+ opts, title="这台机器还没初始化。选择你的身份,或新建一个:"
410
+ )
411
+ if kind == "cancel":
412
+ print("已取消初始化。")
413
+ return BUSINESS
414
+ if kind == "new":
336
415
  prompt_name = input("输入用户名:").strip()
416
+ if not prompt_name:
417
+ print("未输入用户名,已取消。")
418
+ return BUSINESS
337
419
  res = init_identity(repo, claim=None, new=True, name=prompt_name)
338
420
  else:
339
421
  res = init_identity(
340
- repo, claim=opts[int(choice) - 1]["uuid"], new=False, name=""
422
+ repo, claim=opts[idx]["uuid"], new=False, name=""
341
423
  )
342
424
  else:
343
425
  # 名册空:交互输入名字;非终端交给用户去终端跑(init_identity 会报需要名字)