@ryan_nookpi/pi-extension-todo-write-overlay 0.1.1 → 0.2.0

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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/index.ts +60 -5
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -20,6 +20,7 @@ pi install npm:@ryan_nookpi/pi-extension-todo-write-overlay
20
20
  - 세션 단위 todo 상태 저장 및 복원
21
21
  - 우측 상단 오버레이 렌더링
22
22
  - 입력 포커스를 뺏지 않는 `nonCapturing` overlay 사용
23
+ - `/todo-overlay show` / `/todo-overlay hide` 명령으로 오버레이 표시 토글
23
24
  - 진행 중 작업 스피너 표시
24
25
  - 완료/진행/대기 상태별 색상과 아이콘 표시
25
26
  - `notes`는 상태에는 보존하지만 오버레이에는 표시하지 않음
@@ -37,6 +38,17 @@ pi install npm:@ryan_nookpi/pi-extension-todo-write-overlay
37
38
 
38
39
  기존 `todo-write`의 `완료 +2` 같은 완료 항목 접기 로직은 사용하지 않습니다. 완료된 항목도 모두 그대로 표시합니다.
39
40
 
41
+ ## 명령어
42
+
43
+ ```text
44
+ /todo-overlay show
45
+ /todo-overlay hide
46
+ ```
47
+
48
+ - 기본값은 `show`입니다.
49
+ - `hide`는 todo 상태와 reminder 주입은 유지하고, 우측 상단 오버레이 UI만 숨깁니다.
50
+ - 인자 없이 `/todo-overlay`를 실행하면 현재 표시 상태를 알려줍니다.
51
+
40
52
  ## 사용 예
41
53
 
42
54
  ```json
package/index.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { StringEnum } from "@mariozechner/pi-ai";
2
- import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
3
- import type { OverlayHandle, TUI } from "@mariozechner/pi-tui";
4
- import { Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
1
+ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
2
+ import type { OverlayHandle, TUI } from "@earendil-works/pi-tui";
3
+ import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
5
4
  import { type Static, Type } from "@sinclair/typebox";
6
5
 
7
6
  type TodoStatus = "pending" | "in_progress" | "completed";
@@ -25,7 +24,7 @@ type TodoOverlayRecord = {
25
24
  close?: () => void;
26
25
  };
27
26
 
28
- const StatusEnum = StringEnum(["pending", "in_progress", "completed"] as const, {
27
+ const StatusEnum = Type.Union([Type.Literal("pending"), Type.Literal("in_progress"), Type.Literal("completed")], {
29
28
  description: "작업 상태",
30
29
  });
31
30
 
@@ -53,6 +52,7 @@ const todoStateStore = new Map<string, TodoState>();
53
52
  const todoOverlayStore = new Map<string, TodoOverlayRecord>();
54
53
  const todoOverlayMetaStore = new Map<string, { completedAt?: number; completedTurn?: number }>();
55
54
  const todoOverlayAgentRunningStore = new Map<string, boolean>();
55
+ const todoOverlayHiddenStore = new Map<string, boolean>();
56
56
  const todoTurnStore = new Map<string, number>();
57
57
  const TODO_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
58
58
  const TODO_SPINNER_INTERVAL_MS = 120;
@@ -327,6 +327,23 @@ function setTodoOverlayAgentRunning(ctx: Pick<ExtensionContext, "cwd" | "session
327
327
  todoOverlayAgentRunningStore.set(key, running);
328
328
  }
329
329
 
330
+ export type TodoOverlayCommandAction = "show" | "hide" | "status" | "invalid";
331
+
332
+ export function parseTodoOverlayCommand(args: string): TodoOverlayCommandAction {
333
+ const action = args.trim().toLowerCase();
334
+ if (action === "") return "status";
335
+ if (action === "show" || action === "hide") return action;
336
+ return "invalid";
337
+ }
338
+
339
+ function isTodoOverlayHidden(key: string): boolean {
340
+ return todoOverlayHiddenStore.get(key) ?? false;
341
+ }
342
+
343
+ function setTodoOverlayHidden(key: string, hidden: boolean): void {
344
+ todoOverlayHiddenStore.set(key, hidden);
345
+ }
346
+
330
347
  function hideTodoOverlay(key: string): void {
331
348
  const record = todoOverlayStore.get(key);
332
349
  if (!record) return;
@@ -510,10 +527,47 @@ async function syncTodoOverlay(ctx: ExtensionContext, pi: Pick<ExtensionAPI, "ap
510
527
  return;
511
528
  }
512
529
 
530
+ if (isTodoOverlayHidden(key)) {
531
+ hideTodoOverlay(key);
532
+ return;
533
+ }
534
+
513
535
  showOrUpdateTodoOverlay(ctx, key, state);
514
536
  }
515
537
 
516
538
  export default function todoWriteOverlayExtension(pi: ExtensionAPI): void {
539
+ pi.registerCommand("todo-overlay", {
540
+ description: "Show or hide the todo overlay. Usage: /todo-overlay show|hide",
541
+ getArgumentCompletions(prefix: string) {
542
+ const filtered = ["show", "hide"].filter((value) => value.startsWith(prefix.trim().toLowerCase()));
543
+ return filtered.length > 0 ? filtered.map((value) => ({ value, label: value })) : null;
544
+ },
545
+ async handler(args, ctx) {
546
+ const key = getTodoStateKey(ctx);
547
+ const action = parseTodoOverlayCommand(args);
548
+
549
+ if (action === "invalid") {
550
+ ctx.ui.notify("사용법: /todo-overlay show 또는 /todo-overlay hide", "warning");
551
+ return;
552
+ }
553
+
554
+ if (action === "status") {
555
+ ctx.ui.notify(`todo overlay: ${isTodoOverlayHidden(key) ? "hidden" : "shown"}`, "info");
556
+ return;
557
+ }
558
+
559
+ setTodoOverlayHidden(key, action === "hide");
560
+ if (action === "hide") {
561
+ hideTodoOverlay(key);
562
+ ctx.ui.notify("todo overlay를 숨겼습니다. /todo-overlay show로 다시 표시할 수 있습니다.", "info");
563
+ return;
564
+ }
565
+
566
+ await syncTodoOverlay(ctx, pi);
567
+ ctx.ui.notify("todo overlay를 표시합니다.", "info");
568
+ },
569
+ });
570
+
517
571
  pi.registerTool({
518
572
  name: "todo_write",
519
573
  label: "할 일 관리",
@@ -642,6 +696,7 @@ export default function todoWriteOverlayExtension(pi: ExtensionAPI): void {
642
696
  hideTodoOverlay(key);
643
697
  todoOverlayMetaStore.delete(key);
644
698
  todoOverlayAgentRunningStore.delete(key);
699
+ todoOverlayHiddenStore.delete(key);
645
700
  todoTurnStore.delete(key);
646
701
  });
647
702
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryan_nookpi/pi-extension-todo-write-overlay",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Top-right overlay todo_write tool extension for pi.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,9 +28,9 @@
28
28
  ]
29
29
  },
30
30
  "peerDependencies": {
31
- "@mariozechner/pi-ai": "*",
32
- "@mariozechner/pi-coding-agent": "*",
33
- "@mariozechner/pi-tui": "*",
31
+ "@earendil-works/pi-ai": "*",
32
+ "@earendil-works/pi-coding-agent": "*",
33
+ "@earendil-works/pi-tui": "*",
34
34
  "@sinclair/typebox": "*"
35
35
  },
36
36
  "publishConfig": {