@agimon-ai/doompi-task 0.0.1-alpha.40 → 0.0.1-alpha.42

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 CHANGED
@@ -11,7 +11,7 @@ Task owns task records and `tasks.json`. Team owns agents and runs. When both ar
11
11
  ## Requirements
12
12
 
13
13
  - Node.js 22.19.0 or newer
14
- - Pi 0.84.3 and Pi TUI 0.84.3
14
+ - Pi 0.84.4 and Pi TUI 0.84.4
15
15
 
16
16
  ## Install
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-task",
3
- "version": "0.0.1-alpha.40",
3
+ "version": "0.0.1-alpha.42",
4
4
  "description": "Persistent, dependency-aware task graphs and subagent delegation for Pi coding sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -165,15 +165,15 @@
165
165
  "dependencies": {
166
166
  "@deepseek-ai/cordis": "4.0.1",
167
167
  "typebox": "1.3.16",
168
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.40",
169
- "@agimon-ai/doompi-ui": "0.0.1-alpha.40",
170
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.40",
171
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.1",
172
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.3"
168
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.42",
169
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.42",
170
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.42",
171
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.5",
172
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.3"
173
173
  },
174
174
  "devDependencies": {
175
- "@earendil-works/pi-coding-agent": "0.84.3",
176
- "@earendil-works/pi-tui": "0.84.3",
175
+ "@earendil-works/pi-coding-agent": "0.84.4",
176
+ "@earendil-works/pi-tui": "0.84.4",
177
177
  "@tanstack/react-store": "0.11.1",
178
178
  "@tanstack/store": "0.11.1",
179
179
  "@types/node": "26.2.0",
@@ -185,8 +185,8 @@
185
185
  "vitest": "4.1.11"
186
186
  },
187
187
  "peerDependencies": {
188
- "@earendil-works/pi-coding-agent": "0.84.3",
189
- "@earendil-works/pi-tui": "0.84.3"
188
+ "@earendil-works/pi-coding-agent": "0.84.4",
189
+ "@earendil-works/pi-tui": "0.84.4"
190
190
  },
191
191
  "peerDependenciesMeta": {
192
192
  "@earendil-works/pi-coding-agent": {
@@ -3,12 +3,20 @@ import {
3
3
  ChevronDownIcon,
4
4
  ChevronRightIcon,
5
5
  Dot,
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ EditIcon,
11
+ KebabIcon,
12
+ MessageIcon,
6
13
  Textarea,
14
+ TrashIcon,
7
15
  type DotTone,
8
16
  } from '@agimon-ai/doompi-web-components';
9
17
  import type { WebPluginSlotProps } from '@agimon-ai/doompi-web-contracts';
10
18
  import { useStore } from '@tanstack/react-store';
11
- import { useState } from 'react';
19
+ import { useRef, useState } from 'react';
12
20
  import type { WebTask } from '../src/types/webTasks.ts';
13
21
  import { requestTaskInstruction, requestTaskRemoval, tasks, type TaskInstructionKind } from './tasksStore.ts';
14
22
 
@@ -40,6 +48,8 @@ function TaskRow({
40
48
  setEditor: (editor: EditorState | undefined) => void;
41
49
  }) {
42
50
  const editing = editor?.taskId === task.id;
51
+ const editorRequested = useRef(false);
52
+ const editorInput = useRef<HTMLTextAreaElement>(null);
43
53
  const detail = task.status === 'in_progress' ? task.activeForm : task.description;
44
54
  return (
45
55
  <div
@@ -47,14 +57,69 @@ function TaskRow({
47
57
  data-task-status={task.status}
48
58
  className="flex flex-col gap-1 rounded-[5px] px-1 py-1"
49
59
  >
50
- <div className="flex min-w-0 items-center gap-1.5">
60
+ <div className="grid min-w-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-1.5">
51
61
  <Dot tone={STATUS_TONE[task.status]} pulse={task.status === 'in_progress'} />
52
62
  <span
53
- className={`min-w-0 flex-1 truncate text-[10px] font-bold ${task.status === 'failed' ? 'text-doom-red' : task.status === 'completed' ? 'text-doom-dim' : 'text-doom-hi'}`}
63
+ data-testid={`activity-task-title-${task.id}`}
64
+ className={`min-w-0 truncate text-[10px] font-bold ${task.status === 'failed' ? 'text-doom-red' : task.status === 'completed' ? 'text-doom-dim' : 'text-doom-hi'}`}
54
65
  >
55
66
  <span className="text-doom-faint">#{task.id}</span> {task.subject}
56
67
  </span>
57
68
  <span className="shrink-0 text-[8px] text-doom-faint">{task.status.replace('_', ' ')}</span>
69
+ {task.status !== 'completed' && task.status !== 'deleted' ? (
70
+ <DropdownMenu>
71
+ <DropdownMenuTrigger asChild>
72
+ <Button
73
+ variant="ghost"
74
+ size="icon"
75
+ data-testid={`activity-task-menu-${task.id}`}
76
+ aria-label={`task #${task.id} actions`}
77
+ title={`task #${task.id} actions`}
78
+ className="self-center shrink-0 text-doom-faint hover:bg-doom-deep hover:text-doom-hi data-[state=open]:bg-doom-deep data-[state=open]:text-doom-hi"
79
+ >
80
+ <KebabIcon className="h-3 w-3" />
81
+ </Button>
82
+ </DropdownMenuTrigger>
83
+ <DropdownMenuContent
84
+ data-testid={`activity-task-menu-list-${task.id}`}
85
+ onCloseAutoFocus={(event) => {
86
+ if (!editorRequested.current) return;
87
+ editorRequested.current = false;
88
+ event.preventDefault();
89
+ editorInput.current?.focus();
90
+ }}
91
+ >
92
+ <DropdownMenuItem
93
+ data-testid={`activity-task-edit-${task.id}`}
94
+ onSelect={() => {
95
+ editorRequested.current = true;
96
+ setEditor({ taskId: task.id, kind: 'edit', value: '' });
97
+ }}
98
+ >
99
+ <EditIcon className="h-3 w-3" />
100
+ Edit
101
+ </DropdownMenuItem>
102
+ <DropdownMenuItem
103
+ data-testid={`activity-task-note-${task.id}`}
104
+ onSelect={() => {
105
+ editorRequested.current = true;
106
+ setEditor({ taskId: task.id, kind: 'note', value: '' });
107
+ }}
108
+ >
109
+ <MessageIcon className="h-3 w-3" />
110
+ Note
111
+ </DropdownMenuItem>
112
+ <DropdownMenuItem
113
+ variant="destructive"
114
+ data-testid={`activity-task-remove-${task.id}`}
115
+ onSelect={() => requestTaskRemoval(sendSessionFrame, sessionId, task.id)}
116
+ >
117
+ <TrashIcon className="h-3 w-3" />
118
+ Remove
119
+ </DropdownMenuItem>
120
+ </DropdownMenuContent>
121
+ </DropdownMenu>
122
+ ) : null}
58
123
  </div>
59
124
  {detail || task.delegation?.agent || task.blockedBy.length > 0 ? (
60
125
  <span className="truncate pl-3 text-[9px] text-doom-faint">
@@ -63,34 +128,6 @@ function TaskRow({
63
128
  {task.blockedBy.length > 0 ? ` · blocked by ${task.blockedBy.map((id) => `#${id}`).join(', ')}` : ''}
64
129
  </span>
65
130
  ) : null}
66
- {task.status !== 'completed' && task.status !== 'deleted' ? (
67
- <div className="flex items-center gap-2 pl-3">
68
- <Button
69
- variant="link"
70
- size="xs"
71
- className="h-auto px-0 text-[8px]"
72
- onClick={() => setEditor({ taskId: task.id, kind: 'edit', value: '' })}
73
- >
74
- edit
75
- </Button>
76
- <Button
77
- variant="link"
78
- size="xs"
79
- className="h-auto px-0 text-[8px]"
80
- onClick={() => setEditor({ taskId: task.id, kind: 'note', value: '' })}
81
- >
82
- note
83
- </Button>
84
- <Button
85
- variant="link"
86
- size="xs"
87
- className="h-auto px-0 text-[8px] text-doom-red"
88
- onClick={() => requestTaskRemoval(sendSessionFrame, sessionId, task.id)}
89
- >
90
- remove
91
- </Button>
92
- </div>
93
- ) : null}
94
131
  {editing ? (
95
132
  <form
96
133
  className="flex flex-col gap-1 pl-3"
@@ -102,6 +139,7 @@ function TaskRow({
102
139
  }}
103
140
  >
104
141
  <Textarea
142
+ ref={editorInput}
105
143
  autoFocus
106
144
  rows={2}
107
145
  size="sm"