@aemonge-dev/pi-stardeck 0.11.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.
@@ -0,0 +1,100 @@
1
+ export const OPEN_SPEC_FOCUS_ENTRY_TYPE = 'galactica-status.openspec-focus.v1';
2
+
3
+ export type TaskOpenSpecFocus = {
4
+ mode: 'task';
5
+ change: string;
6
+ taskId: string;
7
+ awaitingValidation: boolean;
8
+ };
9
+
10
+ export type OpenSpecFocus = { mode: 'none' } | TaskOpenSpecFocus;
11
+
12
+ export const NO_OPEN_SPEC_FOCUS: OpenSpecFocus = { mode: 'none' };
13
+
14
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/gu;
15
+
16
+ function isRecord(value: unknown): value is Record<string, unknown> {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+
20
+ export function normalizeTitlePart(value: string): string {
21
+ return value.replace(CONTROL_CHARACTERS, ' ').replace(/\s+/gu, ' ').trim();
22
+ }
23
+
24
+ function explicitId(value: unknown): string | undefined {
25
+ if (typeof value !== 'string') return undefined;
26
+ const normalized = normalizeTitlePart(value);
27
+ return normalized || undefined;
28
+ }
29
+
30
+ export function taskFocus(
31
+ change: unknown,
32
+ taskId: unknown,
33
+ awaitingValidation?: unknown,
34
+ ): TaskOpenSpecFocus | undefined {
35
+ const normalizedChange = explicitId(change);
36
+ const normalizedTaskId = explicitId(taskId);
37
+ if (!normalizedChange || !normalizedTaskId) return undefined;
38
+ return {
39
+ mode: 'task',
40
+ change: normalizedChange,
41
+ taskId: normalizedTaskId,
42
+ awaitingValidation: awaitingValidation === true,
43
+ };
44
+ }
45
+
46
+ export function parseOpenSpecFocus(value: unknown): OpenSpecFocus | undefined {
47
+ if (!isRecord(value)) return undefined;
48
+ if (value.mode === 'none') return NO_OPEN_SPEC_FOCUS;
49
+ if (value.mode !== 'task') return undefined;
50
+ return taskFocus(value.change, value.taskId, value.awaitingValidation);
51
+ }
52
+
53
+ export function restoreOpenSpecFocus(entries: readonly unknown[]): OpenSpecFocus {
54
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
55
+ const entry = entries[index];
56
+ if (
57
+ !isRecord(entry) ||
58
+ entry.type !== 'custom' ||
59
+ entry.customType !== OPEN_SPEC_FOCUS_ENTRY_TYPE
60
+ ) {
61
+ continue;
62
+ }
63
+ const focus = parseOpenSpecFocus(entry.data);
64
+ if (focus) return focus;
65
+ }
66
+ return NO_OPEN_SPEC_FOCUS;
67
+ }
68
+
69
+ export function taskflowRunFocus(input: unknown): TaskOpenSpecFocus | undefined {
70
+ if (!isRecord(input) || input.action !== 'run' || !isRecord(input.args)) {
71
+ return undefined;
72
+ }
73
+ const focus = taskFocus(input.args.change, input.args.task);
74
+ return focus && focus.change !== 'none' && focus.taskId !== 'none'
75
+ ? focus
76
+ : undefined;
77
+ }
78
+
79
+ /** Task identity (mode · change · taskId) — the replace-safeguard's
80
+ * question; the validation flag is state, not identity. */
81
+ export function sameTaskFocus(left: OpenSpecFocus, right: OpenSpecFocus): boolean {
82
+ if (left.mode !== right.mode) return false;
83
+ return (
84
+ left.mode === 'none' ||
85
+ (right.mode === 'task' &&
86
+ left.change === right.change &&
87
+ left.taskId === right.taskId)
88
+ );
89
+ }
90
+
91
+ export function sameOpenSpecFocus(left: OpenSpecFocus, right: OpenSpecFocus): boolean {
92
+ if (left.mode !== right.mode) return false;
93
+ return (
94
+ left.mode === 'none' ||
95
+ (right.mode === 'task' &&
96
+ left.change === right.change &&
97
+ left.taskId === right.taskId &&
98
+ left.awaitingValidation === right.awaitingValidation)
99
+ );
100
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
2
 
3
3
  import galacticaContextHeader from '../packages/pi-stardeck/index.js';
4
+ import workState from '../packages/work-state/index.js';
4
5
  import galacticaStatus from '../packages/status/index.js';
5
6
  import footerTelemetry from '../packages/footer/index.js';
6
7
  import { registerStardeckHealth } from './health.js';
@@ -19,6 +20,7 @@ export const COCKPIT_COMPOSITION = [
19
20
  export default function piStardeck(pi: ExtensionAPI): void {
20
21
  registerPiStatus(pi);
21
22
  footerTelemetry(pi);
23
+ workState(pi);
22
24
  galacticaStatus(pi);
23
25
  galacticaContextHeader(pi, {
24
26
  surface: COCKPIT_SURFACES.contextHeader,