@aemonge-dev/pi-stardeck 0.10.0 → 0.12.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.
- package/package.json +3 -2
- package/packages/pi-stardeck/README.md +33 -66
- package/packages/pi-stardeck/index.ts +39 -202
- package/packages/pi-stardeck/src/deck.ts +15 -64
- package/packages/pi-stardeck/src/gauge.ts +1 -40
- package/packages/status/README.md +4 -4
- package/packages/status/index.ts +25 -321
- package/packages/status/src/active-work.ts +2 -58
- package/packages/status/src/ledger.ts +1 -3
- package/packages/status/src/openspec.ts +14 -8
- package/packages/status/src/publisher.ts +6 -76
- package/packages/status/src/reducer.ts +1 -19
- package/packages/status/src/types.ts +0 -1
- package/packages/work-state/README.md +33 -0
- package/packages/work-state/index.ts +160 -0
- package/packages/work-state/package.json +33 -0
- package/packages/work-state/src/focus.ts +100 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aemonge-dev/pi-stardeck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "A Vim- and tmux-inspired command deck for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -48,10 +48,11 @@
|
|
|
48
48
|
"check:openspec": "node scripts/check-openspec.mjs",
|
|
49
49
|
"test:tooling": "node --import tsx/esm --test test/*.test.ts",
|
|
50
50
|
"test:status": "npm --prefix packages/status test",
|
|
51
|
+
"test:work-state": "npm --prefix packages/work-state test",
|
|
51
52
|
"test:stardeck": "npm --prefix packages/pi-stardeck test",
|
|
52
53
|
"test:footer": "npm --prefix packages/footer test",
|
|
53
54
|
"test:vim": "npm --prefix packages/pi-vim-top-border test",
|
|
54
|
-
"test:components": "npm run test:status && npm run test:stardeck && npm run test:footer && npm run test:vim"
|
|
55
|
+
"test:components": "npm run test:status && npm run test:work-state && npm run test:stardeck && npm run test:footer && npm run test:vim"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"@earendil-works/pi-ai": "*",
|
|
@@ -4,79 +4,46 @@ A dependency-free Pi extension that coordinates the prompt information architect
|
|
|
4
4
|
through Pi's supported widget and event APIs.
|
|
5
5
|
|
|
6
6
|
```text
|
|
7
|
-
─ ›
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
─ › ────────────────────────────────────────────────────────────────────
|
|
8
|
+
[] ~/projects/example › main › +0 ~1 1 Opus › high › 42%
|
|
9
|
+
──────────────────────────────────────────────────────────────────────
|
|
10
|
+
Stabilize runtime and focus in the cockpit planning 6/7 › 9/19
|
|
11
|
+
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
|
|
12
|
+
editing deck.ts 00:38'00 · 4.2k
|
|
13
|
+
└ (00'12) scout working
|
|
14
|
+
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
|
|
15
|
+
5h 52% › 3% › 48% › 2 › 268M › 38% › $5.16 abcd1234
|
|
15
16
|
```
|
|
16
17
|
|
|
17
|
-
The
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
and
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
and
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
The title has four styled groups: lifecycle, a structured active ancestry, focus, and
|
|
41
|
-
optional focus detail. Each activity-path segment receives the blue minor `›`; the
|
|
42
|
-
violet-pink major `⟩` divides runtime ancestry from the green narrative focus. Human
|
|
43
|
-
prompt text is never converted into focus automatically. OpenSpec may add its exact Task
|
|
44
|
-
after another green `›`. Focus selection prefers an exact OpenSpec Task, then
|
|
45
|
-
Session Work. Active turns without a selection use the explicit `OpenSpec › no focus`,
|
|
46
|
-
`no OpenSpec`, or `no focus` state supplied by the status producer; `Direct response` is
|
|
47
|
-
never presented as a focus. Empty states are dim while real focus remains bold semantic
|
|
48
|
-
green.
|
|
49
|
-
|
|
50
|
-
Wide rows render lifecycle → active ancestry → optional focus → optional focus detail,
|
|
51
|
-
for example `assure › checking quality › assuring tests ⟩ Implement global flow`. The
|
|
52
|
-
renderer generates every separator. Empty or rejected paths leave no separator or
|
|
53
|
-
placeholder whitespace. As width falls it tries full labels, compact labels, a collapsed
|
|
54
|
-
`… › deepest` ancestry, and finally compact lifecycle plus the deepest active segment.
|
|
55
|
-
Narrative focus disappears before meaningful active ancestry. Terminal display-cell
|
|
56
|
-
measurement and final truncation remain Unicode- and Nerd-Font-aware.
|
|
57
|
-
|
|
58
|
-
Runtime anchor and lifecycle are bold blue `#076678`; activity ancestry and its minor
|
|
59
|
-
`›` are regular blue; the major `⟩` is purple; focus is bold semantic success green; and
|
|
60
|
-
optional focus hierarchy is regular success green. Dim is reserved for the two empty
|
|
61
|
-
focus fallbacks; muted, text, cyan, yellow, and gray are never used in the title.
|
|
62
|
-
|
|
63
|
-
The extension hides Pi's separate built-in `Working...` loader row with
|
|
64
|
-
`ctx.ui.setWorkingVisible(false)`, including its vertical space. The header therefore
|
|
65
|
-
uses its complete width for lifecycle, bounded activity, and focus. Runtime age is
|
|
66
|
-
published for the bottom prompt rail as an eight-cell chronometer measuring time since
|
|
67
|
-
the most recent observed foreground provider, assistant, or tool event. It stays
|
|
68
|
-
fixed-width as it advances: `00:00'00`, `00:00'45`, `00:12'30`, or `12:34'56`; hour/day
|
|
69
|
-
fallbacks also remain eight cells. Active timing uses the high-contrast deep-cyan
|
|
70
|
-
`accent` color; settled states display dim `00:00'00` rather than repeating the header's
|
|
71
|
-
`wait`, `ready`, or other lifecycle word. A session-scoped ticker publishes only when
|
|
72
|
-
the formatted chronometer changes and is cleared on shutdown. No message content,
|
|
73
|
-
thinking, reasoning, prompts, tool arguments, or child-agent output enters the widget.
|
|
18
|
+
The deck renders four stable rows above the editor:
|
|
19
|
+
|
|
20
|
+
1. **Where and what** — identity and companion chips lead, then the project path
|
|
21
|
+
and Git segment; the right corner carries the engine group (model › thinking ›
|
|
22
|
+
quota) and any alert.
|
|
23
|
+
2. **Focus** — the session subject (auto-titled from the first real prompt,
|
|
24
|
+
settable through the `title` tool) with the OpenSpec work tile on the right.
|
|
25
|
+
3. **Pure activity band (optional)** — exists only while an observed activity is
|
|
26
|
+
live — `editing`, `reading`, `grepping`, `bash`, or `subagent` — with the turn
|
|
27
|
+
chronometer and token count riding on the right; subagent and bash child runs
|
|
28
|
+
keep their `└` span rows directly beneath. Between tools and at rest the band
|
|
29
|
+
retires entirely: Pi's own `Working…` indicator stays visible exactly as the
|
|
30
|
+
core renders it, and no phase words or `awaiting Human` filler are ever
|
|
31
|
+
generated.
|
|
32
|
+
4. **Machine and status** — quota, CPU/RAM, context, compactions, and session
|
|
33
|
+
cost yield right-to-left into the width the status end leaves: notices when
|
|
34
|
+
present, otherwise the dim ladybug sentinel wearing the short session id.
|
|
35
|
+
|
|
36
|
+
Rows appear only when they speak — a silent band and spanless state collapse
|
|
37
|
+
their seam. Every slot is bounded, sanitized display vocabulary; no message
|
|
38
|
+
content, thinking, reasoning, prompts, tool arguments, or child-agent output
|
|
39
|
+
enters the widget. Terminal display-cell measurement and final truncation remain
|
|
40
|
+
Unicode- and Nerd-Font-aware.
|
|
74
41
|
|
|
75
42
|
The additive header protocol projects selection independently from live work:
|
|
76
43
|
|
|
77
44
|
```ts
|
|
78
45
|
selection: {
|
|
79
|
-
source: 'openspec' | '
|
|
46
|
+
source: 'openspec' | 'subject' | 'legacy';
|
|
80
47
|
titles: string[]; // one or two bounded, normalized titles
|
|
81
48
|
color: HeaderColor;
|
|
82
49
|
} | null;
|
|
@@ -3,32 +3,16 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
Theme,
|
|
5
5
|
} from '@earendil-works/pi-coding-agent';
|
|
6
|
-
import { truncateToWidth } from '@earendil-works/pi-tui';
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
|
-
buildAgentFooterWidgets,
|
|
10
|
-
buildPromptStatusSnapshot,
|
|
11
|
-
COMPACTION_WIDGET_ID,
|
|
12
|
-
CONTEXT_WIDGET_ID,
|
|
13
8
|
countCompactions,
|
|
14
|
-
ENVIRONMENT_WIDGET_ID,
|
|
15
|
-
FANCY_FOOTER_READY_CHANNEL,
|
|
16
9
|
FOOTER_TELEMETRY_CHANNEL,
|
|
17
|
-
FANCY_FOOTER_WIDGET_CHANNEL,
|
|
18
10
|
formatFooterBranch,
|
|
19
11
|
formatFooterPath,
|
|
20
12
|
GALACTICA_HEADER_CHANNEL,
|
|
21
|
-
GIT_BRANCH_WIDGET_ID,
|
|
22
|
-
GIT_CONFLICT_WIDGET_ID,
|
|
23
|
-
GIT_SCOPE_SEPARATOR_WIDGET_ID,
|
|
24
|
-
GIT_MODIFIED_WIDGET_ID,
|
|
25
|
-
GIT_STAGED_WIDGET_ID,
|
|
26
|
-
GIT_UNTRACKED_WIDGET_ID,
|
|
27
13
|
type FooterTelemetrySnapshot,
|
|
28
14
|
type GitStatusSummary,
|
|
29
15
|
type HeaderSnapshot,
|
|
30
|
-
LEGACY_GIT_WIDGET_ID,
|
|
31
|
-
LOCATION_WIDGET_ID,
|
|
32
16
|
normalizeContextPercent,
|
|
33
17
|
parseFooterTelemetry,
|
|
34
18
|
parseGitBranch,
|
|
@@ -37,24 +21,12 @@ import {
|
|
|
37
21
|
parsePiStatusSnapshot,
|
|
38
22
|
PI_NATIVE_STATUS_CHANNEL,
|
|
39
23
|
PI_STATUS_SNAPSHOT_CHANNEL,
|
|
40
|
-
PROMPT_STATUS_CHANNEL,
|
|
41
|
-
REASONING_WIDGET_ID,
|
|
42
|
-
SCOPE_SEPARATOR_WIDGET_ID,
|
|
43
|
-
runtimeHeaderWork,
|
|
44
24
|
parseVimMode,
|
|
45
25
|
VIM_MODE_CHANNEL,
|
|
46
26
|
type PiStatusSnapshot,
|
|
47
27
|
type VimMode,
|
|
48
28
|
} from './src/gauge.ts';
|
|
49
29
|
import {
|
|
50
|
-
buildRuntimeFooterWidgets,
|
|
51
|
-
formatRailChronometer,
|
|
52
|
-
RUNTIME_SCOPE_SEPARATOR_WIDGET_ID,
|
|
53
|
-
RUNTIME_STATUS_WIDGET_ID,
|
|
54
|
-
} from './src/activity-age.ts';
|
|
55
|
-
import {
|
|
56
|
-
buildCapabilityFooterWidget,
|
|
57
|
-
CAPABILITY_WIDGET_ID,
|
|
58
30
|
LSP_STATUS_EVENT,
|
|
59
31
|
MCP_STATUS_EVENT,
|
|
60
32
|
parseLspCapability,
|
|
@@ -64,11 +36,8 @@ import {
|
|
|
64
36
|
} from './src/capabilities.ts';
|
|
65
37
|
import { shouldRefreshGitAfterTool } from './src/git-refresh.ts';
|
|
66
38
|
import {
|
|
67
|
-
buildResourceFooterWidgets,
|
|
68
39
|
buildResourceTelemetry,
|
|
69
40
|
calculateCpuPercent,
|
|
70
|
-
CPU_WIDGET_ID,
|
|
71
|
-
MEMORY_WIDGET_ID,
|
|
72
41
|
RESOURCE_REFRESH_MS,
|
|
73
42
|
sampleProcessTreeResources,
|
|
74
43
|
updateCpuSmoothing,
|
|
@@ -77,7 +46,6 @@ import {
|
|
|
77
46
|
type ResourceTelemetry,
|
|
78
47
|
} from './src/process-resources.ts';
|
|
79
48
|
import { renderHeaderDeck } from './src/deck.ts';
|
|
80
|
-
import { layoutLifecycleTitle, renderLifecycleTitleSections } from './src/title.ts';
|
|
81
49
|
|
|
82
50
|
const WIDGET_KEY = 'galactica.work-identity';
|
|
83
51
|
const PI_STATUS_SOURCE_CHANNEL = 'pi-stardeck:status-source/v1';
|
|
@@ -103,7 +71,11 @@ export interface ContextHeaderDeckSurface {
|
|
|
103
71
|
}
|
|
104
72
|
|
|
105
73
|
export interface ContextHeaderOptions {
|
|
106
|
-
surface
|
|
74
|
+
/** The deck surface is the product: `'deck'` registers the above-editor
|
|
75
|
+
* widget, `'editor-deck'` renders into an embedder's component instead
|
|
76
|
+
* (vim border). The pre-deck legacy title line is retired
|
|
77
|
+
* (simplify-deck-defaults). */
|
|
78
|
+
surface?: 'deck' | 'editor-deck';
|
|
107
79
|
deckSurface?: ContextHeaderDeckSurface;
|
|
108
80
|
}
|
|
109
81
|
|
|
@@ -164,7 +136,6 @@ export default function galacticaContextHeader(
|
|
|
164
136
|
pi: ExtensionAPI,
|
|
165
137
|
options: ContextHeaderOptions = {},
|
|
166
138
|
): void {
|
|
167
|
-
const deckMode = options.surface === 'deck' || options.surface === 'editor-deck';
|
|
168
139
|
const editorDeckSurface =
|
|
169
140
|
options.surface === 'editor-deck' ? options.deckSurface : undefined;
|
|
170
141
|
let clearWidget: (() => void) | undefined;
|
|
@@ -176,7 +147,6 @@ export default function galacticaContextHeader(
|
|
|
176
147
|
let header: HeaderSnapshot | null = null;
|
|
177
148
|
let agentBusy = false;
|
|
178
149
|
let lastObservedActivityAt: number | null = null;
|
|
179
|
-
let lastActivityAgeSignature = '';
|
|
180
150
|
let activityAgeTimer: ReturnType<typeof setInterval> | undefined;
|
|
181
151
|
let sessionGeneration = 0;
|
|
182
152
|
let sessionShortId: string | undefined;
|
|
@@ -196,64 +166,9 @@ export default function galacticaContextHeader(
|
|
|
196
166
|
let piStatus: PiStatusSnapshot | undefined;
|
|
197
167
|
let nativeStatusBridgeAvailable = true;
|
|
198
168
|
let vimMode: VimMode = 'insert';
|
|
199
|
-
let lastCapabilitySignature = '';
|
|
200
169
|
|
|
201
170
|
const currentHeader = () => header;
|
|
202
171
|
|
|
203
|
-
const publishPromptStatus = () => {
|
|
204
|
-
if (deckMode) return;
|
|
205
|
-
pi.events.emit(
|
|
206
|
-
PROMPT_STATUS_CHANNEL,
|
|
207
|
-
buildPromptStatusSnapshot(currentHeader(), branch, gitStatus),
|
|
208
|
-
);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const publishFooter = () => {
|
|
212
|
-
if (deckMode) return;
|
|
213
|
-
const ctx = activeCtx;
|
|
214
|
-
if (!ctx) return;
|
|
215
|
-
const usage = ctx.getContextUsage();
|
|
216
|
-
const contextPercent = normalizeContextPercent(
|
|
217
|
-
usage?.percent,
|
|
218
|
-
usage?.tokens,
|
|
219
|
-
usage?.contextWindow ?? ctx.model?.contextWindow,
|
|
220
|
-
);
|
|
221
|
-
const thinking = pi.getThinkingLevel();
|
|
222
|
-
for (const message of buildAgentFooterWidgets({
|
|
223
|
-
cwd: formatFooterPath(activeCwd || ctx.cwd, process.env.HOME),
|
|
224
|
-
devbox: process.env.DEVBOX_PROJECT_ROOT !== undefined,
|
|
225
|
-
branch: formatFooterBranch(branch),
|
|
226
|
-
git: gitStatus,
|
|
227
|
-
gitAvailable,
|
|
228
|
-
model: ctx.model?.name || ctx.model?.id || 'no model',
|
|
229
|
-
...(thinking && thinking !== 'off' ? { thinking } : {}),
|
|
230
|
-
contextPercent,
|
|
231
|
-
compactionCount: countCompactions(ctx.sessionManager.getBranch()),
|
|
232
|
-
})) {
|
|
233
|
-
pi.events.emit(FANCY_FOOTER_WIDGET_CHANNEL, message);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
const publishLatestResourceFooter = () => {
|
|
238
|
-
if (deckMode || !latestResourceTelemetry) return;
|
|
239
|
-
for (const message of buildResourceFooterWidgets(latestResourceTelemetry)) {
|
|
240
|
-
pi.events.emit(FANCY_FOOTER_WIDGET_CHANNEL, message);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const publishCapabilityFooter = (force = false) => {
|
|
245
|
-
if (deckMode) return;
|
|
246
|
-
const signature = [mcpCapability?.healthy ?? '', mcpCapability?.total ?? ''].join(
|
|
247
|
-
':',
|
|
248
|
-
);
|
|
249
|
-
if (!force && signature === lastCapabilitySignature) return;
|
|
250
|
-
lastCapabilitySignature = signature;
|
|
251
|
-
pi.events.emit(
|
|
252
|
-
FANCY_FOOTER_WIDGET_CHANNEL,
|
|
253
|
-
buildCapabilityFooterWidget(mcpCapability),
|
|
254
|
-
);
|
|
255
|
-
};
|
|
256
|
-
|
|
257
172
|
const refreshResources = async (): Promise<void> => {
|
|
258
173
|
if (!activeCtx || resourceRefreshInFlight) return;
|
|
259
174
|
resourceRefreshInFlight = true;
|
|
@@ -274,58 +189,28 @@ export default function galacticaContextHeader(
|
|
|
274
189
|
latestResourceTelemetry = telemetry;
|
|
275
190
|
if (signature === lastResourceSignature) return;
|
|
276
191
|
lastResourceSignature = signature;
|
|
277
|
-
|
|
278
|
-
else publishLatestResourceFooter();
|
|
192
|
+
requestRender?.();
|
|
279
193
|
} finally {
|
|
280
194
|
resourceRefreshInFlight = false;
|
|
281
195
|
}
|
|
282
196
|
};
|
|
283
197
|
|
|
284
|
-
const removeFooterWidgets = () => {
|
|
285
|
-
for (const id of [
|
|
286
|
-
LOCATION_WIDGET_ID,
|
|
287
|
-
LEGACY_GIT_WIDGET_ID,
|
|
288
|
-
GIT_BRANCH_WIDGET_ID,
|
|
289
|
-
GIT_SCOPE_SEPARATOR_WIDGET_ID,
|
|
290
|
-
GIT_STAGED_WIDGET_ID,
|
|
291
|
-
GIT_MODIFIED_WIDGET_ID,
|
|
292
|
-
GIT_UNTRACKED_WIDGET_ID,
|
|
293
|
-
GIT_CONFLICT_WIDGET_ID,
|
|
294
|
-
ENVIRONMENT_WIDGET_ID,
|
|
295
|
-
REASONING_WIDGET_ID,
|
|
296
|
-
CONTEXT_WIDGET_ID,
|
|
297
|
-
COMPACTION_WIDGET_ID,
|
|
298
|
-
CPU_WIDGET_ID,
|
|
299
|
-
MEMORY_WIDGET_ID,
|
|
300
|
-
RUNTIME_STATUS_WIDGET_ID,
|
|
301
|
-
RUNTIME_SCOPE_SEPARATOR_WIDGET_ID,
|
|
302
|
-
SCOPE_SEPARATOR_WIDGET_ID,
|
|
303
|
-
CAPABILITY_WIDGET_ID,
|
|
304
|
-
]) {
|
|
305
|
-
pi.events.emit(FANCY_FOOTER_WIDGET_CHANNEL, {
|
|
306
|
-
protocol: 1,
|
|
307
|
-
type: 'remove',
|
|
308
|
-
id,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
|
|
313
198
|
const currentActivityAge = (): number | null =>
|
|
314
199
|
agentBusy && lastObservedActivityAt !== null
|
|
315
200
|
? Math.max(0, Date.now() - lastObservedActivityAt)
|
|
316
201
|
: null;
|
|
317
202
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
203
|
+
const startActivityTicker = (): void => {
|
|
204
|
+
// Duty-cycled: the 50 ms chronometer ticks only while an activity can
|
|
205
|
+
// render — idle sessions cost zero wakeups (simplify-deck-defaults).
|
|
206
|
+
if (activityAgeTimer) return;
|
|
207
|
+
activityAgeTimer = setInterval(refreshActivityAge, 50);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const stopActivityTicker = (): void => {
|
|
211
|
+
if (activityAgeTimer === undefined) return;
|
|
212
|
+
clearInterval(activityAgeTimer);
|
|
213
|
+
activityAgeTimer = undefined;
|
|
329
214
|
};
|
|
330
215
|
|
|
331
216
|
const publishCapabilityHealth = () => {
|
|
@@ -363,12 +248,6 @@ export default function galacticaContextHeader(
|
|
|
363
248
|
});
|
|
364
249
|
};
|
|
365
250
|
|
|
366
|
-
const stopReady = pi.events.on(FANCY_FOOTER_READY_CHANNEL, () => {
|
|
367
|
-
publishFooter();
|
|
368
|
-
publishLatestResourceFooter();
|
|
369
|
-
publishRuntimeFooter();
|
|
370
|
-
publishCapabilityFooter(true);
|
|
371
|
-
});
|
|
372
251
|
const stopLspStatus = pi.events.on(LSP_STATUS_EVENT, (raw) => {
|
|
373
252
|
lspCapability = parseLspCapability(raw);
|
|
374
253
|
requestRender?.();
|
|
@@ -377,7 +256,6 @@ export default function galacticaContextHeader(
|
|
|
377
256
|
const stopMcpStatus = pi.events.on(MCP_STATUS_EVENT, (raw) => {
|
|
378
257
|
mcpCapability = parseMcpCapability(raw);
|
|
379
258
|
requestRender?.();
|
|
380
|
-
publishCapabilityFooter();
|
|
381
259
|
publishCapabilityHealth();
|
|
382
260
|
});
|
|
383
261
|
const stopFooterTelemetry = pi.events.on(FOOTER_TELEMETRY_CHANNEL, (raw) => {
|
|
@@ -401,33 +279,28 @@ export default function galacticaContextHeader(
|
|
|
401
279
|
const stopHeader = pi.events.on(GALACTICA_HEADER_CHANNEL, (raw) => {
|
|
402
280
|
header = parseHeaderSnapshot(raw);
|
|
403
281
|
requestRender?.();
|
|
404
|
-
publishPromptStatus();
|
|
405
|
-
publishRuntimeFooter();
|
|
406
282
|
});
|
|
407
283
|
|
|
408
284
|
const markObservedActivity = () => {
|
|
409
285
|
lastObservedActivityAt = Date.now();
|
|
410
|
-
|
|
286
|
+
startActivityTicker();
|
|
411
287
|
requestRender?.();
|
|
412
|
-
publishRuntimeFooter();
|
|
413
288
|
};
|
|
414
289
|
|
|
415
290
|
const clearObservedActivity = () => {
|
|
416
291
|
lastObservedActivityAt = null;
|
|
417
|
-
|
|
292
|
+
stopActivityTicker();
|
|
418
293
|
};
|
|
419
294
|
|
|
420
295
|
const refreshActivityAge = () => {
|
|
421
296
|
if (currentActivityAge() === null) return;
|
|
422
|
-
|
|
423
|
-
else publishRuntimeFooter();
|
|
297
|
+
requestRender?.();
|
|
424
298
|
};
|
|
425
299
|
|
|
426
300
|
pi.on('before_agent_start', (_event, ctx) => {
|
|
427
301
|
if (ctx.mode !== 'tui') return;
|
|
428
302
|
agentBusy = true;
|
|
429
303
|
markObservedActivity();
|
|
430
|
-
publishPromptStatus();
|
|
431
304
|
});
|
|
432
305
|
|
|
433
306
|
pi.on('turn_start', markObservedActivity);
|
|
@@ -480,11 +353,7 @@ export default function galacticaContextHeader(
|
|
|
480
353
|
gitStatus = gitAvailable
|
|
481
354
|
? parseGitStatus(String(statusResult?.stdout ?? ''))
|
|
482
355
|
: { ...EMPTY_GIT };
|
|
483
|
-
|
|
484
|
-
else {
|
|
485
|
-
publishPromptStatus();
|
|
486
|
-
publishFooter();
|
|
487
|
-
}
|
|
356
|
+
requestRender?.();
|
|
488
357
|
} while (gitRefreshQueued && activeCwd);
|
|
489
358
|
} finally {
|
|
490
359
|
gitRefreshInFlight = false;
|
|
@@ -495,9 +364,8 @@ export default function galacticaContextHeader(
|
|
|
495
364
|
clearWidget?.();
|
|
496
365
|
clearFooter?.();
|
|
497
366
|
clearFooter = undefined;
|
|
498
|
-
|
|
367
|
+
stopActivityTicker();
|
|
499
368
|
if (resourceTimer) clearInterval(resourceTimer);
|
|
500
|
-
activityAgeTimer = undefined;
|
|
501
369
|
resourceTimer = undefined;
|
|
502
370
|
++sessionGeneration;
|
|
503
371
|
activeCtx = ctx;
|
|
@@ -519,34 +387,23 @@ export default function galacticaContextHeader(
|
|
|
519
387
|
cpuSmoothing = undefined;
|
|
520
388
|
latestResourceTelemetry = undefined;
|
|
521
389
|
lastResourceSignature = '';
|
|
522
|
-
lastCapabilitySignature = '';
|
|
523
390
|
clearObservedActivity();
|
|
524
|
-
publishFooter();
|
|
525
|
-
publishPromptStatus();
|
|
526
|
-
publishRuntimeFooter();
|
|
527
|
-
publishCapabilityFooter();
|
|
528
391
|
publishCapabilityHealth();
|
|
529
|
-
if (ctx.mode === 'tui') {
|
|
530
|
-
ctx.ui.setWorkingVisible(false);
|
|
531
|
-
activityAgeTimer = setInterval(refreshActivityAge, 50);
|
|
532
|
-
}
|
|
533
392
|
if (!ctx.hasUI) return;
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
(
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
);
|
|
549
|
-
}
|
|
393
|
+
clearFooter = installEmptyDeckFooter(
|
|
394
|
+
ctx,
|
|
395
|
+
(statuses) => {
|
|
396
|
+
pi.events.emit(PI_NATIVE_STATUS_CHANNEL, {
|
|
397
|
+
protocol: 1,
|
|
398
|
+
type: 'snapshot',
|
|
399
|
+
statuses,
|
|
400
|
+
});
|
|
401
|
+
},
|
|
402
|
+
(available) => {
|
|
403
|
+
nativeStatusBridgeAvailable = available;
|
|
404
|
+
publishCapabilityHealth();
|
|
405
|
+
},
|
|
406
|
+
);
|
|
550
407
|
void refreshResources();
|
|
551
408
|
resourceTimer = setInterval(() => void refreshResources(), RESOURCE_REFRESH_MS);
|
|
552
409
|
activeCwd = ctx.cwd;
|
|
@@ -610,16 +467,7 @@ export default function galacticaContextHeader(
|
|
|
610
467
|
requestRender = () => tui.requestRender();
|
|
611
468
|
return {
|
|
612
469
|
render(width: number): string[] {
|
|
613
|
-
|
|
614
|
-
const { work, idle } = runtimeHeaderWork(currentHeader()?.work, agentBusy);
|
|
615
|
-
const layout = layoutLifecycleTitle(work, width, agentBusy);
|
|
616
|
-
const line = renderLifecycleTitleSections(
|
|
617
|
-
layout,
|
|
618
|
-
work.lifecycle,
|
|
619
|
-
idle,
|
|
620
|
-
theme,
|
|
621
|
-
);
|
|
622
|
-
return [truncateToWidth(line, width, '…')];
|
|
470
|
+
return renderDeck(width, theme);
|
|
623
471
|
},
|
|
624
472
|
invalidate() {},
|
|
625
473
|
};
|
|
@@ -634,21 +482,17 @@ export default function galacticaContextHeader(
|
|
|
634
482
|
});
|
|
635
483
|
|
|
636
484
|
pi.on('model_select', () => {
|
|
637
|
-
publishFooter();
|
|
638
485
|
requestRender?.();
|
|
639
486
|
});
|
|
640
487
|
|
|
641
488
|
pi.on('thinking_level_select', () => {
|
|
642
|
-
|
|
643
|
-
else publishFooter();
|
|
489
|
+
requestRender?.();
|
|
644
490
|
});
|
|
645
491
|
pi.on('message_end', (event) => {
|
|
646
492
|
if (event.message.role === 'assistant') markObservedActivity();
|
|
647
|
-
publishFooter();
|
|
648
493
|
});
|
|
649
494
|
pi.on('session_compact', () => {
|
|
650
|
-
|
|
651
|
-
else publishFooter();
|
|
495
|
+
requestRender?.();
|
|
652
496
|
});
|
|
653
497
|
|
|
654
498
|
pi.on('tool_execution_end', (event) => {
|
|
@@ -662,9 +506,6 @@ export default function galacticaContextHeader(
|
|
|
662
506
|
agentBusy = false;
|
|
663
507
|
clearObservedActivity();
|
|
664
508
|
requestRender?.();
|
|
665
|
-
publishPromptStatus();
|
|
666
|
-
publishFooter();
|
|
667
|
-
publishRuntimeFooter();
|
|
668
509
|
void refreshGit();
|
|
669
510
|
});
|
|
670
511
|
|
|
@@ -675,9 +516,7 @@ export default function galacticaContextHeader(
|
|
|
675
516
|
gitAvailable = false;
|
|
676
517
|
agentBusy = false;
|
|
677
518
|
clearObservedActivity();
|
|
678
|
-
if (activityAgeTimer) clearInterval(activityAgeTimer);
|
|
679
519
|
if (resourceTimer) clearInterval(resourceTimer);
|
|
680
|
-
activityAgeTimer = undefined;
|
|
681
520
|
resourceTimer = undefined;
|
|
682
521
|
resourceRefreshInFlight = false;
|
|
683
522
|
previousResourceSample = undefined;
|
|
@@ -689,9 +528,7 @@ export default function galacticaContextHeader(
|
|
|
689
528
|
clearWidget = undefined;
|
|
690
529
|
clearFooter?.();
|
|
691
530
|
clearFooter = undefined;
|
|
692
|
-
removeFooterWidgets();
|
|
693
531
|
stopHeader();
|
|
694
|
-
stopReady();
|
|
695
532
|
stopLspStatus();
|
|
696
533
|
stopMcpStatus();
|
|
697
534
|
stopPiStatus();
|