pi-mission-control 0.0.0-dev
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 +205 -0
- package/agents/auditor.md +45 -0
- package/agents/worker.md +44 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +526 -0
- package/dist/index.js.map +1 -0
- package/dist/state.d.ts +265 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +474 -0
- package/dist/state.js.map +1 -0
- package/dist/tools/add-phase.d.ts +28 -0
- package/dist/tools/add-phase.d.ts.map +1 -0
- package/dist/tools/add-phase.js +69 -0
- package/dist/tools/add-phase.js.map +1 -0
- package/dist/tools/add-task.d.ts +30 -0
- package/dist/tools/add-task.d.ts.map +1 -0
- package/dist/tools/add-task.js +85 -0
- package/dist/tools/add-task.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/init.d.ts +34 -0
- package/dist/tools/init.d.ts.map +1 -0
- package/dist/tools/init.js +75 -0
- package/dist/tools/init.js.map +1 -0
- package/dist/tools/mission-complete.d.ts +30 -0
- package/dist/tools/mission-complete.d.ts.map +1 -0
- package/dist/tools/mission-complete.js +85 -0
- package/dist/tools/mission-complete.js.map +1 -0
- package/dist/tools/mission-resume.d.ts +35 -0
- package/dist/tools/mission-resume.d.ts.map +1 -0
- package/dist/tools/mission-resume.js +87 -0
- package/dist/tools/mission-resume.js.map +1 -0
- package/dist/tools/scaffold.d.ts +24 -0
- package/dist/tools/scaffold.d.ts.map +1 -0
- package/dist/tools/scaffold.js +129 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/tools/update-phase.d.ts +33 -0
- package/dist/tools/update-phase.d.ts.map +1 -0
- package/dist/tools/update-phase.js +101 -0
- package/dist/tools/update-phase.js.map +1 -0
- package/dist/tools/update-task.d.ts +34 -0
- package/dist/tools/update-task.d.ts.map +1 -0
- package/dist/tools/update-task.js +104 -0
- package/dist/tools/update-task.js.map +1 -0
- package/dist/tui/dashboard.d.ts +146 -0
- package/dist/tui/dashboard.d.ts.map +1 -0
- package/dist/tui/dashboard.js +381 -0
- package/dist/tui/dashboard.js.map +1 -0
- package/dist/tui/header.d.ts +39 -0
- package/dist/tui/header.d.ts.map +1 -0
- package/dist/tui/header.js +62 -0
- package/dist/tui/header.js.map +1 -0
- package/dist/tui/idle-view.d.ts +44 -0
- package/dist/tui/idle-view.d.ts.map +1 -0
- package/dist/tui/idle-view.js +87 -0
- package/dist/tui/idle-view.js.map +1 -0
- package/dist/tui/index.d.ts +13 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +15 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/past-runs.d.ts +49 -0
- package/dist/tui/past-runs.d.ts.map +1 -0
- package/dist/tui/past-runs.js +207 -0
- package/dist/tui/past-runs.js.map +1 -0
- package/dist/tui/phases-panel.d.ts +46 -0
- package/dist/tui/phases-panel.d.ts.map +1 -0
- package/dist/tui/phases-panel.js +161 -0
- package/dist/tui/phases-panel.js.map +1 -0
- package/dist/tui/progress-bar.d.ts +37 -0
- package/dist/tui/progress-bar.d.ts.map +1 -0
- package/dist/tui/progress-bar.js +123 -0
- package/dist/tui/progress-bar.js.map +1 -0
- package/dist/tui/styles.d.ts +8 -0
- package/dist/tui/styles.d.ts.map +1 -0
- package/dist/tui/styles.js +22 -0
- package/dist/tui/styles.js.map +1 -0
- package/dist/tui/tasks-panel.d.ts +48 -0
- package/dist/tui/tasks-panel.d.ts.map +1 -0
- package/dist/tui/tasks-panel.js +191 -0
- package/dist/tui/tasks-panel.js.map +1 -0
- package/package.json +42 -0
- package/skills/mission-memory/SKILL.md +88 -0
- package/skills/mission-orchestrator/SKILL.md +167 -0
- package/skills/mission-pm/SKILL.md +83 -0
- package/skills/mission-research/SKILL.md +66 -0
- package/skills/mission-tech-lead/SKILL.md +68 -0
- package/src/index.ts +659 -0
- package/src/state.ts +623 -0
- package/src/tools/add-phase.ts +98 -0
- package/src/tools/add-task.ts +121 -0
- package/src/tools/index.ts +18 -0
- package/src/tools/init.ts +109 -0
- package/src/tools/mission-complete.ts +118 -0
- package/src/tools/mission-resume.ts +119 -0
- package/src/tools/scaffold.ts +167 -0
- package/src/tools/update-phase.ts +140 -0
- package/src/tools/update-task.ts +145 -0
- package/src/tui/dashboard.ts +441 -0
- package/src/tui/header.ts +85 -0
- package/src/tui/idle-view.ts +114 -0
- package/src/tui/index.ts +20 -0
- package/src/tui/past-runs.ts +261 -0
- package/src/tui/phases-panel.ts +199 -0
- package/src/tui/progress-bar.ts +152 -0
- package/src/tui/styles.ts +27 -0
- package/src/tui/tasks-panel.ts +228 -0
- package/templates/state.json +5 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mission Control TUI Tasks Panel
|
|
3
|
+
*
|
|
4
|
+
* Right panel displaying:
|
|
5
|
+
* - Tasks for the selected phase
|
|
6
|
+
* - Status icons including spinner for in-progress
|
|
7
|
+
* - Navigation indicator
|
|
8
|
+
*
|
|
9
|
+
* Icons (Nerd Font):
|
|
10
|
+
* - Done: \uf058 () check
|
|
11
|
+
* - In Progress: \uf1ce () spinner/sync
|
|
12
|
+
* - Pending: \uf096 () empty box
|
|
13
|
+
* - Failed: \uf057 () times
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
17
|
+
import type { Task, Phase } from "../state.js";
|
|
18
|
+
import { truncateToWidth } from "@mariozechner/pi-tui";
|
|
19
|
+
import { missionSuccess, missionWarning, strike } from "./styles.js";
|
|
20
|
+
|
|
21
|
+
export interface TasksPanelProps {
|
|
22
|
+
phase: Phase | null;
|
|
23
|
+
tasks: Task[];
|
|
24
|
+
selectedIndex: number;
|
|
25
|
+
focused: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Render a single task line
|
|
30
|
+
* Styling: selected=white bold, running=orange/warning, unselected non-running=gray/muted
|
|
31
|
+
*/
|
|
32
|
+
function renderTaskLine(
|
|
33
|
+
task: Task,
|
|
34
|
+
index: number,
|
|
35
|
+
selectedIndex: number,
|
|
36
|
+
width: number,
|
|
37
|
+
theme: Theme,
|
|
38
|
+
focused: boolean
|
|
39
|
+
): string {
|
|
40
|
+
const isSelected = focused && index === selectedIndex;
|
|
41
|
+
const isRunning = task.status === "in_progress";
|
|
42
|
+
const baseText = `Task ${index + 1}: ${task.name}`;
|
|
43
|
+
|
|
44
|
+
// Apply styling based on selection and running state
|
|
45
|
+
let styled: string;
|
|
46
|
+
if (task.status === "removed") {
|
|
47
|
+
const removedText = strike(baseText);
|
|
48
|
+
styled = isSelected
|
|
49
|
+
? theme.fg("text", theme.bold(removedText))
|
|
50
|
+
: theme.fg("muted", removedText);
|
|
51
|
+
} else if (isSelected) {
|
|
52
|
+
styled = theme.fg("text", theme.bold(baseText));
|
|
53
|
+
} else if (isRunning) {
|
|
54
|
+
styled = missionWarning(baseText);
|
|
55
|
+
} else if (task.status === "done") {
|
|
56
|
+
styled = missionSuccess(baseText);
|
|
57
|
+
} else if (task.status === "failed") {
|
|
58
|
+
styled = theme.fg("error", baseText);
|
|
59
|
+
} else {
|
|
60
|
+
styled = theme.fg("muted", baseText);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return truncateToWidth(styled, width);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Render the tasks panel
|
|
68
|
+
*/
|
|
69
|
+
export function renderTasksPanel(
|
|
70
|
+
width: number,
|
|
71
|
+
height: number,
|
|
72
|
+
props: TasksPanelProps,
|
|
73
|
+
theme: Theme
|
|
74
|
+
): string[] {
|
|
75
|
+
const { phase, tasks, selectedIndex, focused } = props;
|
|
76
|
+
const lines: string[] = [];
|
|
77
|
+
|
|
78
|
+
const header = theme.fg("text", theme.bold("TASKS"));
|
|
79
|
+
lines.push(truncateToWidth(header, width));
|
|
80
|
+
|
|
81
|
+
if (tasks.length === 0) {
|
|
82
|
+
const emptyMsg = phase
|
|
83
|
+
? theme.fg("muted", " No tasks in this phase")
|
|
84
|
+
: theme.fg("muted", " Select a phase to view tasks");
|
|
85
|
+
lines.push(truncateToWidth(emptyMsg, width));
|
|
86
|
+
} else {
|
|
87
|
+
// Calculate visible range
|
|
88
|
+
const availableHeight = height - 1;
|
|
89
|
+
let startIdx = 0;
|
|
90
|
+
let endIdx = tasks.length;
|
|
91
|
+
|
|
92
|
+
if (tasks.length > availableHeight) {
|
|
93
|
+
const halfHeight = Math.floor(availableHeight / 2);
|
|
94
|
+
startIdx = Math.max(0, selectedIndex - halfHeight);
|
|
95
|
+
endIdx = Math.min(tasks.length, startIdx + availableHeight);
|
|
96
|
+
|
|
97
|
+
if (endIdx - startIdx < availableHeight) {
|
|
98
|
+
startIdx = Math.max(0, endIdx - availableHeight);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Render visible tasks
|
|
103
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
104
|
+
const task = tasks[i];
|
|
105
|
+
const line = renderTaskLine(task, i, selectedIndex, width, theme, focused);
|
|
106
|
+
lines.push(line);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Pad to height
|
|
111
|
+
while (lines.length < height) {
|
|
112
|
+
lines.push("");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return lines.slice(0, height);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Tasks panel component class
|
|
120
|
+
* Tracks per-phase selection state to avoid auto-selecting when switching phases
|
|
121
|
+
*/
|
|
122
|
+
export class TasksPanelComponent {
|
|
123
|
+
private props: TasksPanelProps;
|
|
124
|
+
private cachedWidth?: number;
|
|
125
|
+
private cachedHeight?: number;
|
|
126
|
+
private cachedLines?: string[];
|
|
127
|
+
private phaseSelections: Map<string, number> = new Map(); // phase id -> selected index
|
|
128
|
+
|
|
129
|
+
constructor(phase: Phase | null = null, focused = false) {
|
|
130
|
+
this.props = {
|
|
131
|
+
phase,
|
|
132
|
+
tasks: phase?.tasks ?? [],
|
|
133
|
+
selectedIndex: -1,
|
|
134
|
+
focused
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
update(phase: Phase | null, selectedIndex?: number): void {
|
|
139
|
+
const prevPhaseId = this.props.phase?.id;
|
|
140
|
+
|
|
141
|
+
// Save current selection for previous phase
|
|
142
|
+
if (prevPhaseId && this.props.tasks.length > 0) {
|
|
143
|
+
this.phaseSelections.set(prevPhaseId, this.props.selectedIndex);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.props.phase = phase;
|
|
147
|
+
this.props.tasks = phase?.tasks ?? [];
|
|
148
|
+
|
|
149
|
+
if (selectedIndex !== undefined) {
|
|
150
|
+
this.props.selectedIndex = Math.max(-1, Math.min(selectedIndex, this.props.tasks.length - 1));
|
|
151
|
+
} else {
|
|
152
|
+
const phaseId = phase?.id;
|
|
153
|
+
if (phaseId && this.phaseSelections.has(phaseId)) {
|
|
154
|
+
const savedIndex = this.phaseSelections.get(phaseId)!;
|
|
155
|
+
this.props.selectedIndex = Math.max(-1, Math.min(savedIndex, this.props.tasks.length - 1));
|
|
156
|
+
} else if (this.props.focused && this.props.tasks.length > 0) {
|
|
157
|
+
const inProgressIdx = this.props.tasks.findIndex(t => t.status === "in_progress");
|
|
158
|
+
this.props.selectedIndex = inProgressIdx !== -1 ? inProgressIdx : 0;
|
|
159
|
+
} else {
|
|
160
|
+
this.props.selectedIndex = -1;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
this.invalidate();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setFocused(focused: boolean): void {
|
|
167
|
+
this.props.focused = focused;
|
|
168
|
+
if (focused && this.props.selectedIndex < 0 && this.props.tasks.length > 0) {
|
|
169
|
+
const inProgressIdx = this.props.tasks.findIndex(t => t.status === "in_progress");
|
|
170
|
+
this.props.selectedIndex = inProgressIdx !== -1 ? inProgressIdx : 0;
|
|
171
|
+
}
|
|
172
|
+
this.invalidate();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
isFocused(): boolean {
|
|
176
|
+
return this.props.focused;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
getSelectedIndex(): number {
|
|
180
|
+
return this.props.selectedIndex;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getSelectedTask(): Task | undefined {
|
|
184
|
+
if (this.props.selectedIndex < 0) return undefined;
|
|
185
|
+
return this.props.tasks[this.props.selectedIndex];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
navigateUp(): void {
|
|
189
|
+
if (this.props.selectedIndex < 0 && this.props.tasks.length > 0) {
|
|
190
|
+
this.props.selectedIndex = 0;
|
|
191
|
+
this.invalidate();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (this.props.selectedIndex > 0) {
|
|
195
|
+
this.props.selectedIndex--;
|
|
196
|
+
this.invalidate();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
navigateDown(): void {
|
|
201
|
+
if (this.props.selectedIndex < 0 && this.props.tasks.length > 0) {
|
|
202
|
+
this.props.selectedIndex = 0;
|
|
203
|
+
this.invalidate();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (this.props.selectedIndex < this.props.tasks.length - 1) {
|
|
207
|
+
this.props.selectedIndex++;
|
|
208
|
+
this.invalidate();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
render(width: number, height: number, theme: Theme): string[] {
|
|
213
|
+
if (this.cachedLines && this.cachedWidth === width && this.cachedHeight === height) {
|
|
214
|
+
return this.cachedLines;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.cachedLines = renderTasksPanel(width, height, this.props, theme);
|
|
218
|
+
this.cachedWidth = width;
|
|
219
|
+
this.cachedHeight = height;
|
|
220
|
+
return this.cachedLines;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
invalidate(): void {
|
|
224
|
+
this.cachedWidth = undefined;
|
|
225
|
+
this.cachedHeight = undefined;
|
|
226
|
+
this.cachedLines = undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|