@ryan_nookpi/pi-extension-todo-write 0.1.0 → 0.1.2
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/README.md +59 -6
- package/index.ts +47 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,68 @@
|
|
|
1
1
|
# @ryan_nookpi/pi-extension-todo-write
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
pi가 현재 세션에서 구조화된 작업 목록을 만들고 갱신할 수 있게 해주는 `todo_write` 익스텐션입니다.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 설치
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pi install /Users/creatrip/Documents/pi-extension/packages/todo-write
|
|
9
8
|
pi install npm:@ryan_nookpi/pi-extension-todo-write
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
##
|
|
11
|
+
## 무엇을 해결하나
|
|
13
12
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- 큰 작업을 여러 단계로 나눠서 관리
|
|
14
|
+
- 현재 진행 중인 작업을 사용자에게 명확히 보여줌
|
|
15
|
+
- 상태를 `pending` / `in_progress` / `completed`로 일관되게 유지
|
|
16
|
+
- 세션 압축(compaction) 이후에도 남은 작업을 이어서 추적
|
|
17
|
+
|
|
18
|
+
## 언제 쓰면 좋은가
|
|
19
|
+
|
|
20
|
+
- 구현, 디버깅, 리팩터링처럼 단계가 많은 작업
|
|
21
|
+
- 테스트/수정/검증을 따로 추적해야 하는 작업
|
|
22
|
+
- 도중에 요구사항이 바뀌어 계획을 다시 정리해야 하는 작업
|
|
23
|
+
|
|
24
|
+
## 작성 규칙
|
|
25
|
+
|
|
26
|
+
- `content`는 짧은 명령형으로 작성
|
|
27
|
+
- 예: `테스트 실행`, `로그 확인`, `배포 검증`
|
|
28
|
+
- `activeForm`은 현재 진행 중 문구로 작성
|
|
29
|
+
- 예: `테스트 실행 중`, `로그 확인 중`
|
|
30
|
+
- 동시에 `in_progress`인 작업은 하나만 유지
|
|
31
|
+
- 작업이 끝나면 바로 `completed`로 갱신
|
|
32
|
+
- 더 이상 의미 없는 항목은 목록에서 제거
|
|
33
|
+
|
|
34
|
+
## 파라미터 가이드
|
|
35
|
+
|
|
36
|
+
최상위 입력은 아래 형태입니다.
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"todos": [
|
|
41
|
+
{
|
|
42
|
+
"content": "테스트 실행",
|
|
43
|
+
"status": "in_progress",
|
|
44
|
+
"activeForm": "테스트 실행 중",
|
|
45
|
+
"notes": "핵심 시나리오부터 확인"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 필드 설명
|
|
52
|
+
|
|
53
|
+
- `content`: 작업 내용
|
|
54
|
+
- `status`: `pending` | `in_progress` | `completed`
|
|
55
|
+
- `activeForm`: 진행 중일 때 위젯에 보여줄 문구
|
|
56
|
+
- `notes`: 추가 메모
|
|
57
|
+
|
|
58
|
+
## 예시 프롬프트
|
|
59
|
+
|
|
60
|
+
- "작업 목록 만들고 단계별로 진행해줘."
|
|
61
|
+
- "이 작업을 구현/테스트/검증으로 나눠서 추적해줘."
|
|
62
|
+
- "디버깅 플랜을 todo로 관리하면서 진행해줘."
|
|
63
|
+
|
|
64
|
+
## 참고
|
|
65
|
+
|
|
66
|
+
- 이 익스텐션은 `todo_write` 도구 호출 시 내부 상태를 저장합니다.
|
|
67
|
+
- 세션 압축 이후에도 남은 작업이 있으면 이어서 진행할 수 있도록 리마인더를 남깁니다.
|
|
68
|
+
- 완료 항목은 위젯에서 일부만 노출되고, 나머지는 `완료 +N` 형태로 요약될 수 있습니다.
|
package/index.ts
CHANGED
|
@@ -18,23 +18,23 @@ type TodoState = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const StatusEnum = StringEnum(["pending", "in_progress", "completed"] as const, {
|
|
21
|
-
description: "
|
|
21
|
+
description: "작업 상태",
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
const InputTask = Type.Object({
|
|
25
|
-
content: Type.String({ description: "
|
|
25
|
+
content: Type.String({ description: "작업 설명" }),
|
|
26
26
|
status: StatusEnum,
|
|
27
27
|
activeForm: Type.Optional(
|
|
28
28
|
Type.String({
|
|
29
|
-
description: "
|
|
29
|
+
description: "진행 중 표시용 현재진행형 문구 (예: '테스트 실행 중')",
|
|
30
30
|
}),
|
|
31
31
|
),
|
|
32
|
-
notes: Type.Optional(Type.String({ description: "
|
|
32
|
+
notes: Type.Optional(Type.String({ description: "추가 맥락 또는 메모" })),
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
const TodoWriteParams = Type.Object(
|
|
36
36
|
{
|
|
37
|
-
todos: Type.Array(InputTask, { description: "
|
|
37
|
+
todos: Type.Array(InputTask, { description: "업데이트된 todo 목록" }),
|
|
38
38
|
},
|
|
39
39
|
{ additionalProperties: true },
|
|
40
40
|
);
|
|
@@ -174,22 +174,22 @@ export function renderTodoWidgetLines(state: TodoState): string[] {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
export function renderTodoWriteSummary(state: TodoState): string {
|
|
177
|
-
if (state.tasks.length === 0) return "
|
|
177
|
+
if (state.tasks.length === 0) return "할 일 목록을 비웠습니다.";
|
|
178
178
|
|
|
179
179
|
const remainingTasks = state.tasks.filter((task) => task.status === "pending" || task.status === "in_progress");
|
|
180
180
|
const doneCount = state.tasks.filter((task) => task.status === "completed").length;
|
|
181
181
|
|
|
182
182
|
const lines: string[] = [];
|
|
183
183
|
if (remainingTasks.length === 0) {
|
|
184
|
-
lines.push("
|
|
184
|
+
lines.push("남은 항목: 없음.");
|
|
185
185
|
} else {
|
|
186
|
-
lines.push(
|
|
186
|
+
lines.push(`남은 항목 (${remainingTasks.length}개):`);
|
|
187
187
|
for (const task of remainingTasks) {
|
|
188
188
|
lines.push(` - ${task.id} ${task.content} [${task.status}]`);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
lines.push(
|
|
192
|
+
lines.push(`진행률: ${doneCount}/${state.tasks.length} 완료`);
|
|
193
193
|
|
|
194
194
|
for (const task of state.tasks) {
|
|
195
195
|
const marker = task.status === "completed" ? "✓" : task.status === "in_progress" ? "→" : "○";
|
|
@@ -205,17 +205,17 @@ function buildTodoTurnContext(state: TodoState): string | null {
|
|
|
205
205
|
const activeTask = state.tasks.find((task) => task.status === "in_progress");
|
|
206
206
|
const directive = activeTask
|
|
207
207
|
? [
|
|
208
|
-
|
|
209
|
-
"
|
|
208
|
+
`현재 작업: ${activeTask.id} ${activeTask.content}`,
|
|
209
|
+
"이 작업이 끝났다면 다른 도구 호출이나 응답보다 먼저 todo_write로 상태를 갱신하세요.",
|
|
210
210
|
].join("\n")
|
|
211
211
|
: hasRemainingTasks(state)
|
|
212
|
-
? "
|
|
213
|
-
: "
|
|
212
|
+
? "남은 작업이 있지만 현재 in_progress 상태의 항목이 없습니다. 계속 진행하기 전에 todo_write로 다음 활성 작업을 지정하세요."
|
|
213
|
+
: "모든 todo 항목이 완료되었습니다.";
|
|
214
214
|
return [
|
|
215
|
-
"[todo-reminder]
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
"
|
|
215
|
+
"[todo-reminder] 현재 todo_write 상태 스냅샷",
|
|
216
|
+
"출처: todo_write 도구가 유지하는 세션 메모리 상태입니다.",
|
|
217
|
+
"현재 턴에서는 이 내용을 가장 최신의 기준 상태로 간주하세요.",
|
|
218
|
+
"이 스냅샷과 모순되게 설명하지 말고, 상태가 달라졌다면 먼저 todo_write를 업데이트하세요.",
|
|
219
219
|
"",
|
|
220
220
|
summary,
|
|
221
221
|
"",
|
|
@@ -317,8 +317,8 @@ export function restoreTodoWriteState(ctx: Pick<ExtensionContext, "cwd" | "sessi
|
|
|
317
317
|
export function buildPostCompactionTodoReminder(state: TodoState): string | null {
|
|
318
318
|
if (!hasRemainingTasks(state)) return null;
|
|
319
319
|
return [
|
|
320
|
-
"[todo-reminder]
|
|
321
|
-
"
|
|
320
|
+
"[todo-reminder] compaction 이후에도 todo_write에 아직 남은 항목이 있습니다.",
|
|
321
|
+
"아래의 기준 스냅샷을 바탕으로 이어서 작업하세요.",
|
|
322
322
|
"",
|
|
323
323
|
renderTodoWriteSummary(state),
|
|
324
324
|
].join("\n");
|
|
@@ -452,34 +452,34 @@ async function syncTodoWidget(ctx: ExtensionContext, pi: Pick<ExtensionAPI, "app
|
|
|
452
452
|
export default function todoWriteExtension(pi: ExtensionAPI): void {
|
|
453
453
|
pi.registerTool({
|
|
454
454
|
name: "todo_write",
|
|
455
|
-
label: "
|
|
456
|
-
description:
|
|
457
|
-
|
|
458
|
-
##
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
|
|
463
|
-
##
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
|
|
468
|
-
##
|
|
469
|
-
-
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
-
|
|
476
|
-
-
|
|
477
|
-
|
|
478
|
-
##
|
|
479
|
-
- content:
|
|
455
|
+
label: "할 일 관리",
|
|
456
|
+
description: `현재 코딩 세션의 구조화된 작업 목록을 만들고 관리합니다. 진행 상황을 추적하고, 복잡한 요청을 단계로 나누고, 사용자에게 현재 무엇을 하고 있는지 보여줄 때 사용하세요.
|
|
457
|
+
|
|
458
|
+
## 언제 사용할까
|
|
459
|
+
- 3단계 이상의 복잡한 멀티스텝 작업
|
|
460
|
+
- 사용자가 여러 작업을 한 번에 요청한 경우
|
|
461
|
+
- 구현/디버깅 전에 계획 정리가 필요한 비단순 작업
|
|
462
|
+
|
|
463
|
+
## 언제 쓰지 말까
|
|
464
|
+
- 단순한 한 가지 작업이면 바로 수행
|
|
465
|
+
- 3단계 미만으로 끝나는 아주 간단한 작업
|
|
466
|
+
- 순수 대화형/정보 제공성 응답만 필요한 경우
|
|
467
|
+
|
|
468
|
+
## 규칙
|
|
469
|
+
- 가능하면 todo 내용은 한글로 간결하게 작성
|
|
470
|
+
- 작업하면서 상태를 실시간으로 갱신
|
|
471
|
+
- 작업이 끝나면 즉시 completed로 변경하고 몰아서 처리하지 않기
|
|
472
|
+
- in_progress 상태는 정확히 하나만 유지
|
|
473
|
+
- 새 작업을 시작하기 전에 현재 작업을 정리
|
|
474
|
+
- 더 이상 의미 없는 항목은 목록에서 제거
|
|
475
|
+
- 완전히 끝난 일만 completed로 표시하고, 막혔으면 in_progress 유지
|
|
476
|
+
- 요구사항이 바뀌면 계속 진행하기 전에 todo 목록부터 갱신
|
|
477
|
+
|
|
478
|
+
## 필드 설명
|
|
479
|
+
- content: 명령형 작업 문구 (예: "테스트 실행")
|
|
480
480
|
- status: pending | in_progress | completed
|
|
481
|
-
- activeForm: (
|
|
482
|
-
- notes: (
|
|
481
|
+
- activeForm: (선택) 진행 중 표시 문구 (예: "테스트 실행 중")
|
|
482
|
+
- notes: (선택) 추가 맥락`,
|
|
483
483
|
parameters: TodoWriteParams,
|
|
484
484
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
485
485
|
const applied = applyTodoWrite(params.todos);
|
|
@@ -554,7 +554,7 @@ export default function todoWriteExtension(pi: ExtensionAPI): void {
|
|
|
554
554
|
if (!reminder) return;
|
|
555
555
|
|
|
556
556
|
if (ctx.hasUI) {
|
|
557
|
-
ctx.ui.notify("
|
|
557
|
+
ctx.ui.notify("todo 알림: compaction 이후에도 남은 항목이 있습니다.", "info");
|
|
558
558
|
}
|
|
559
559
|
|
|
560
560
|
pi.sendMessage(
|