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.
Files changed (110) hide show
  1. package/README.md +205 -0
  2. package/agents/auditor.md +45 -0
  3. package/agents/worker.md +44 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +526 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/state.d.ts +265 -0
  9. package/dist/state.d.ts.map +1 -0
  10. package/dist/state.js +474 -0
  11. package/dist/state.js.map +1 -0
  12. package/dist/tools/add-phase.d.ts +28 -0
  13. package/dist/tools/add-phase.d.ts.map +1 -0
  14. package/dist/tools/add-phase.js +69 -0
  15. package/dist/tools/add-phase.js.map +1 -0
  16. package/dist/tools/add-task.d.ts +30 -0
  17. package/dist/tools/add-task.d.ts.map +1 -0
  18. package/dist/tools/add-task.js +85 -0
  19. package/dist/tools/add-task.js.map +1 -0
  20. package/dist/tools/index.d.ts +13 -0
  21. package/dist/tools/index.d.ts.map +1 -0
  22. package/dist/tools/index.js +16 -0
  23. package/dist/tools/index.js.map +1 -0
  24. package/dist/tools/init.d.ts +34 -0
  25. package/dist/tools/init.d.ts.map +1 -0
  26. package/dist/tools/init.js +75 -0
  27. package/dist/tools/init.js.map +1 -0
  28. package/dist/tools/mission-complete.d.ts +30 -0
  29. package/dist/tools/mission-complete.d.ts.map +1 -0
  30. package/dist/tools/mission-complete.js +85 -0
  31. package/dist/tools/mission-complete.js.map +1 -0
  32. package/dist/tools/mission-resume.d.ts +35 -0
  33. package/dist/tools/mission-resume.d.ts.map +1 -0
  34. package/dist/tools/mission-resume.js +87 -0
  35. package/dist/tools/mission-resume.js.map +1 -0
  36. package/dist/tools/scaffold.d.ts +24 -0
  37. package/dist/tools/scaffold.d.ts.map +1 -0
  38. package/dist/tools/scaffold.js +129 -0
  39. package/dist/tools/scaffold.js.map +1 -0
  40. package/dist/tools/update-phase.d.ts +33 -0
  41. package/dist/tools/update-phase.d.ts.map +1 -0
  42. package/dist/tools/update-phase.js +101 -0
  43. package/dist/tools/update-phase.js.map +1 -0
  44. package/dist/tools/update-task.d.ts +34 -0
  45. package/dist/tools/update-task.d.ts.map +1 -0
  46. package/dist/tools/update-task.js +104 -0
  47. package/dist/tools/update-task.js.map +1 -0
  48. package/dist/tui/dashboard.d.ts +146 -0
  49. package/dist/tui/dashboard.d.ts.map +1 -0
  50. package/dist/tui/dashboard.js +381 -0
  51. package/dist/tui/dashboard.js.map +1 -0
  52. package/dist/tui/header.d.ts +39 -0
  53. package/dist/tui/header.d.ts.map +1 -0
  54. package/dist/tui/header.js +62 -0
  55. package/dist/tui/header.js.map +1 -0
  56. package/dist/tui/idle-view.d.ts +44 -0
  57. package/dist/tui/idle-view.d.ts.map +1 -0
  58. package/dist/tui/idle-view.js +87 -0
  59. package/dist/tui/idle-view.js.map +1 -0
  60. package/dist/tui/index.d.ts +13 -0
  61. package/dist/tui/index.d.ts.map +1 -0
  62. package/dist/tui/index.js +15 -0
  63. package/dist/tui/index.js.map +1 -0
  64. package/dist/tui/past-runs.d.ts +49 -0
  65. package/dist/tui/past-runs.d.ts.map +1 -0
  66. package/dist/tui/past-runs.js +207 -0
  67. package/dist/tui/past-runs.js.map +1 -0
  68. package/dist/tui/phases-panel.d.ts +46 -0
  69. package/dist/tui/phases-panel.d.ts.map +1 -0
  70. package/dist/tui/phases-panel.js +161 -0
  71. package/dist/tui/phases-panel.js.map +1 -0
  72. package/dist/tui/progress-bar.d.ts +37 -0
  73. package/dist/tui/progress-bar.d.ts.map +1 -0
  74. package/dist/tui/progress-bar.js +123 -0
  75. package/dist/tui/progress-bar.js.map +1 -0
  76. package/dist/tui/styles.d.ts +8 -0
  77. package/dist/tui/styles.d.ts.map +1 -0
  78. package/dist/tui/styles.js +22 -0
  79. package/dist/tui/styles.js.map +1 -0
  80. package/dist/tui/tasks-panel.d.ts +48 -0
  81. package/dist/tui/tasks-panel.d.ts.map +1 -0
  82. package/dist/tui/tasks-panel.js +191 -0
  83. package/dist/tui/tasks-panel.js.map +1 -0
  84. package/package.json +42 -0
  85. package/skills/mission-memory/SKILL.md +88 -0
  86. package/skills/mission-orchestrator/SKILL.md +167 -0
  87. package/skills/mission-pm/SKILL.md +83 -0
  88. package/skills/mission-research/SKILL.md +66 -0
  89. package/skills/mission-tech-lead/SKILL.md +68 -0
  90. package/src/index.ts +659 -0
  91. package/src/state.ts +623 -0
  92. package/src/tools/add-phase.ts +98 -0
  93. package/src/tools/add-task.ts +121 -0
  94. package/src/tools/index.ts +18 -0
  95. package/src/tools/init.ts +109 -0
  96. package/src/tools/mission-complete.ts +118 -0
  97. package/src/tools/mission-resume.ts +119 -0
  98. package/src/tools/scaffold.ts +167 -0
  99. package/src/tools/update-phase.ts +140 -0
  100. package/src/tools/update-task.ts +145 -0
  101. package/src/tui/dashboard.ts +441 -0
  102. package/src/tui/header.ts +85 -0
  103. package/src/tui/idle-view.ts +114 -0
  104. package/src/tui/index.ts +20 -0
  105. package/src/tui/past-runs.ts +261 -0
  106. package/src/tui/phases-panel.ts +199 -0
  107. package/src/tui/progress-bar.ts +152 -0
  108. package/src/tui/styles.ts +27 -0
  109. package/src/tui/tasks-panel.ts +228 -0
  110. 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
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "active_run_id": null,
3
+ "current_phase": "idle",
4
+ "current_status_message": ""
5
+ }