xtrm-tools 0.7.7 → 0.7.9
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/.xtrm/config/hooks.json +0 -3
- package/.xtrm/registry.json +537 -565
- package/.xtrm/skills/default/gitnexus-cli/SKILL.md +82 -0
- package/.xtrm/skills/default/gitnexus-exploring/SKILL.md +78 -0
- package/.xtrm/skills/default/gitnexus-guide/SKILL.md +64 -0
- package/.xtrm/skills/default/init-session/SKILL.md +3 -0
- package/.xtrm/skills/default/last30days/SKILL.md +1 -1
- package/.xtrm/skills/default/last30days/scripts/lib/youtube_yt.py +59 -40
- package/.xtrm/skills/default/sync-docs/references/doc-structure.md +1 -1
- package/.xtrm/skills/default/sync-docs/references/schema.md +1 -1
- package/.xtrm/skills/default/sync-docs/scripts/doc_structure_analyzer.py +2 -2
- package/.xtrm/skills/default/using-specialists/SKILL.md +346 -138
- package/.xtrm/skills/default/using-specialists/SKILL.safe.md +1082 -0
- package/.xtrm/skills/default/using-specialists/SKILL.ultra.md +1082 -0
- package/.xtrm/skills/default/vaultctl/SKILL.md +150 -0
- package/CHANGELOG.md +4 -4
- package/cli/dist/index.cjs +328 -192
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +1 -1
- package/package.json +8 -8
- package/packages/pi-extensions/MIGRATION_NOTES.md +39 -0
- package/packages/pi-extensions/README.md +43 -0
- package/packages/pi-extensions/extensions/README.md +5 -0
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/beads/index.ts +1 -1
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/beads/package.json +1 -4
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-footer/index.ts +1 -1
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-footer/package.json +1 -4
- package/packages/pi-extensions/extensions/custom-provider-qwen-cli/package.json +16 -0
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/pi-serena-compact/package.json +9 -2
- package/{.xtrm/ext-src → packages/pi-extensions/extensions}/quality-gates/index.ts +1 -1
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/quality-gates/package.json +1 -4
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/service-skills/index.ts +1 -1
- package/{.xtrm/ext-src → packages/pi-extensions/extensions}/service-skills/package.json +1 -4
- package/{.xtrm/ext-src → packages/pi-extensions/extensions}/session-flow/index.ts +1 -1
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/session-flow/package.json +1 -4
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-loader/index.ts +1 -1
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-loader/package.json +1 -4
- package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/index.ts +1 -1
- package/packages/pi-extensions/package.json +46 -0
- package/packages/pi-extensions/src/core/README.md +9 -0
- package/{.xtrm/ext-src/core/lib.ts → packages/pi-extensions/src/core/index.ts} +3 -1
- package/packages/pi-extensions/src/extensions/auto-session-name.ts +3 -0
- package/packages/pi-extensions/src/extensions/auto-update.ts +3 -0
- package/packages/pi-extensions/src/extensions/beads.ts +3 -0
- package/packages/pi-extensions/src/extensions/compact-header.ts +3 -0
- package/packages/pi-extensions/src/extensions/custom-footer.ts +3 -0
- package/packages/pi-extensions/src/extensions/custom-provider-qwen-cli.ts +3 -0
- package/packages/pi-extensions/src/extensions/git-checkpoint.ts +3 -0
- package/packages/pi-extensions/src/extensions/lsp-bootstrap.ts +3 -0
- package/packages/pi-extensions/src/extensions/pi-serena-compact.ts +3 -0
- package/packages/pi-extensions/src/extensions/quality-gates.ts +3 -0
- package/packages/pi-extensions/src/extensions/service-skills.ts +3 -0
- package/packages/pi-extensions/src/extensions/session-flow.ts +3 -0
- package/packages/pi-extensions/src/extensions/xtrm-loader.ts +3 -0
- package/packages/pi-extensions/src/extensions/xtrm-ui.ts +3 -0
- package/packages/pi-extensions/src/index.ts +9 -0
- package/packages/pi-extensions/src/registry.ts +52 -0
- package/packages/pi-extensions/src/shared/index.ts +1 -0
- package/packages/pi-extensions/src/shared/legacy-path-map.ts +23 -0
- package/.xtrm/config/pi/extensions/core/package.json +0 -18
- package/.xtrm/config/pi/extensions/custom-provider-qwen-cli/package.json +0 -1
- package/.xtrm/config/pi/extensions/quality-gates/index.ts +0 -66
- package/.xtrm/config/pi/extensions/service-skills/package.json +0 -19
- package/.xtrm/config/pi/extensions/session-flow/index.ts +0 -96
- package/.xtrm/config/pi/extensions/xtrm-ui/themes/pidex-dark.json +0 -89
- package/.xtrm/config/pi/extensions/xtrm-ui/themes/pidex-light.json +0 -89
- package/.xtrm/ext-src/auto-session-name/index.ts +0 -29
- package/.xtrm/ext-src/auto-session-name/package.json +0 -16
- package/.xtrm/ext-src/auto-update/index.ts +0 -71
- package/.xtrm/ext-src/auto-update/package.json +0 -16
- package/.xtrm/ext-src/beads/index.ts +0 -232
- package/.xtrm/ext-src/beads/package.json +0 -19
- package/.xtrm/ext-src/compact-header/index.ts +0 -69
- package/.xtrm/ext-src/compact-header/package.json +0 -16
- package/.xtrm/ext-src/core/adapter.ts +0 -52
- package/.xtrm/ext-src/core/guard-rules.ts +0 -100
- package/.xtrm/ext-src/core/logger.ts +0 -45
- package/.xtrm/ext-src/core/package.json +0 -18
- package/.xtrm/ext-src/core/runner.ts +0 -71
- package/.xtrm/ext-src/core/session-state.ts +0 -59
- package/.xtrm/ext-src/custom-footer/index.ts +0 -398
- package/.xtrm/ext-src/custom-footer/package.json +0 -19
- package/.xtrm/ext-src/custom-provider-qwen-cli/index.ts +0 -363
- package/.xtrm/ext-src/custom-provider-qwen-cli/package.json +0 -1
- package/.xtrm/ext-src/git-checkpoint/index.ts +0 -53
- package/.xtrm/ext-src/git-checkpoint/package.json +0 -16
- package/.xtrm/ext-src/lsp-bootstrap/index.ts +0 -134
- package/.xtrm/ext-src/lsp-bootstrap/package.json +0 -17
- package/.xtrm/ext-src/pi-serena-compact/index.ts +0 -121
- package/.xtrm/ext-src/pi-serena-compact/package.json +0 -16
- package/.xtrm/ext-src/quality-gates/package.json +0 -19
- package/.xtrm/ext-src/service-skills/index.ts +0 -108
- package/.xtrm/ext-src/session-flow/package.json +0 -19
- package/.xtrm/ext-src/xtrm-loader/index.ts +0 -152
- package/.xtrm/ext-src/xtrm-loader/package.json +0 -19
- package/.xtrm/ext-src/xtrm-ui/format.ts +0 -282
- package/.xtrm/ext-src/xtrm-ui/index.ts +0 -1112
- package/.xtrm/ext-src/xtrm-ui/package.json +0 -21
- /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.combined.log +0 -0
- /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.stderr.log +0 -0
- /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.stdout.log +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-session-name/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-session-name/package.json +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-update/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-update/package.json +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/compact-header/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/compact-header/package.json +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-provider-qwen-cli/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/git-checkpoint/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/git-checkpoint/package.json +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/lsp-bootstrap/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/lsp-bootstrap/package.json +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/pi-serena-compact/index.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/format.ts +0 -0
- /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/package.json +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/adapter.ts +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/guard-rules.ts +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/lib.ts +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/logger.ts +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/runner.ts +0 -0
- /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/session-state.ts +0 -0
- /package/{.xtrm/ext-src/xtrm-ui/themes → packages/pi-extensions/themes/xtrm-ui}/pidex-dark.json +0 -0
- /package/{.xtrm/ext-src/xtrm-ui/themes → packages/pi-extensions/themes/xtrm-ui}/pidex-light.json +0 -0
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XTRM Custom Footer Extension
|
|
3
|
-
*
|
|
4
|
-
* Layout:
|
|
5
|
-
* Line 1: ~/path (branch *+↑) — with git status flags, no session name
|
|
6
|
-
* Line 2: XX%/window | (provider) model • thinking — simplified stats
|
|
7
|
-
* Line 3: ◐ 4843.5 Rework project bootstrap... — beads claim or ○ 6 open
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
11
|
-
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
12
|
-
|
|
13
|
-
import { SubprocessRunner, EventAdapter } from "@xtrm/pi-core";
|
|
14
|
-
|
|
15
|
-
export default function (pi: ExtensionAPI) {
|
|
16
|
-
interface BeadClaim {
|
|
17
|
-
id: string;
|
|
18
|
-
shortId: string;
|
|
19
|
-
title: string | null;
|
|
20
|
-
status: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface BeadState {
|
|
24
|
-
claims: BeadClaim[];
|
|
25
|
-
openCount: number;
|
|
26
|
-
lastFetch: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface RuntimeState {
|
|
30
|
-
branch: string | null;
|
|
31
|
-
gitStatus: string;
|
|
32
|
-
lastFetch: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const STATUS_ICONS: Record<string, string> = {
|
|
36
|
-
open: "○",
|
|
37
|
-
in_progress: "◐",
|
|
38
|
-
blocked: "●",
|
|
39
|
-
closed: "✓",
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Chip background colours (raw ANSI — theme has no bg() API)
|
|
43
|
-
const CHIP_BG_NEUTRAL = "\x1b[48;5;238m";
|
|
44
|
-
const CHIP_BG_ACTIVE = "\x1b[48;5;39m";
|
|
45
|
-
const CHIP_BG_BLOCKED = "\x1b[48;5;88m";
|
|
46
|
-
const CHIP_FG = "\x1b[38;5;15m";
|
|
47
|
-
const CHIP_RESET = "\x1b[0m";
|
|
48
|
-
|
|
49
|
-
const STATUS_BG: Record<string, string> = {
|
|
50
|
-
open: CHIP_BG_NEUTRAL,
|
|
51
|
-
in_progress: CHIP_BG_ACTIVE,
|
|
52
|
-
blocked: CHIP_BG_BLOCKED,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const chip = (text: string, bg = CHIP_BG_NEUTRAL): string => `${bg}${CHIP_FG} ${text} ${CHIP_RESET}`;
|
|
56
|
-
|
|
57
|
-
let capturedPi: ExtensionAPI = pi;
|
|
58
|
-
let capturedCtx: any = null;
|
|
59
|
-
let requestRender: (() => void) | null = null;
|
|
60
|
-
|
|
61
|
-
const CACHE_TTL = 5000;
|
|
62
|
-
let refreshingBeads = false;
|
|
63
|
-
let refreshingRuntime = false;
|
|
64
|
-
|
|
65
|
-
let beadState: BeadState = {
|
|
66
|
-
claims: [],
|
|
67
|
-
openCount: 0,
|
|
68
|
-
lastFetch: 0,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
let runtimeState: RuntimeState = {
|
|
72
|
-
branch: null,
|
|
73
|
-
gitStatus: "",
|
|
74
|
-
lastFetch: 0,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const getCwd = () => capturedCtx?.cwd || process.cwd();
|
|
78
|
-
const getShortId = (id: string) => id.split("-").pop() ?? id;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Parse git status --porcelain output into status flags
|
|
82
|
-
*/
|
|
83
|
-
const parseGitFlags = (porcelain: string): string => {
|
|
84
|
-
let modified = false;
|
|
85
|
-
let staged = false;
|
|
86
|
-
let deleted = false;
|
|
87
|
-
for (const line of porcelain.split("\n").filter(Boolean)) {
|
|
88
|
-
if (/^ M|^AM|^MM/.test(line)) modified = true;
|
|
89
|
-
if (/^A |^M /.test(line)) staged = true;
|
|
90
|
-
if (/^ D|^D /.test(line)) deleted = true;
|
|
91
|
-
}
|
|
92
|
-
return `${modified ? "*" : ""}${staged ? "+" : ""}${deleted ? "-" : ""}`;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Fetch git branch and status
|
|
97
|
-
*/
|
|
98
|
-
const refreshRuntimeState = async () => {
|
|
99
|
-
if (refreshingRuntime || Date.now() - runtimeState.lastFetch < CACHE_TTL) return;
|
|
100
|
-
refreshingRuntime = true;
|
|
101
|
-
const cwd = getCwd();
|
|
102
|
-
try {
|
|
103
|
-
let branch: string | null = null;
|
|
104
|
-
let gitStatus = "";
|
|
105
|
-
|
|
106
|
-
const rootResult = await SubprocessRunner.run("git", ["rev-parse", "--show-toplevel"], { cwd });
|
|
107
|
-
const repoRoot = rootResult.code === 0 ? rootResult.stdout.trim() : null;
|
|
108
|
-
|
|
109
|
-
if (repoRoot) {
|
|
110
|
-
const branchResult = await SubprocessRunner.run("git", ["branch", "--show-current"], { cwd });
|
|
111
|
-
branch = branchResult.code === 0 ? branchResult.stdout.trim() || null : null;
|
|
112
|
-
|
|
113
|
-
const porcelainResult = await SubprocessRunner.run(
|
|
114
|
-
"git",
|
|
115
|
-
["--no-optional-locks", "status", "--porcelain"],
|
|
116
|
-
{ cwd },
|
|
117
|
-
);
|
|
118
|
-
const baseFlags = porcelainResult.code === 0 ? parseGitFlags(porcelainResult.stdout) : "";
|
|
119
|
-
|
|
120
|
-
let upstreamFlags = "";
|
|
121
|
-
const abResult = await SubprocessRunner.run(
|
|
122
|
-
"git",
|
|
123
|
-
["--no-optional-locks", "rev-list", "--left-right", "--count", "@{upstream}...HEAD"],
|
|
124
|
-
{ cwd },
|
|
125
|
-
);
|
|
126
|
-
if (abResult.code === 0) {
|
|
127
|
-
const [behindRaw, aheadRaw] = abResult.stdout.trim().split(/\s+/);
|
|
128
|
-
const behind = Number(behindRaw || 0);
|
|
129
|
-
const ahead = Number(aheadRaw || 0);
|
|
130
|
-
if (ahead > 0 && behind > 0) upstreamFlags = "↕";
|
|
131
|
-
else if (ahead > 0) upstreamFlags = "↑";
|
|
132
|
-
else if (behind > 0) upstreamFlags = "↓";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
gitStatus = `${baseFlags}${upstreamFlags}`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
runtimeState = {
|
|
139
|
-
branch,
|
|
140
|
-
gitStatus,
|
|
141
|
-
lastFetch: Date.now(),
|
|
142
|
-
};
|
|
143
|
-
requestRender?.();
|
|
144
|
-
} catch {
|
|
145
|
-
// Fail soft — keep last known runtime state.
|
|
146
|
-
} finally {
|
|
147
|
-
refreshingRuntime = false;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Format token counts (from original footer)
|
|
153
|
-
*/
|
|
154
|
-
const formatTokens = (count: number): string => {
|
|
155
|
-
if (count < 1000) return count.toString();
|
|
156
|
-
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
157
|
-
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
|
158
|
-
if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;
|
|
159
|
-
return `${Math.round(count / 1000000)}M`;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const refreshBeadState = async () => {
|
|
163
|
-
if (refreshingBeads || Date.now() - beadState.lastFetch < CACHE_TTL) return;
|
|
164
|
-
const cwd = getCwd();
|
|
165
|
-
if (!EventAdapter.isBeadsProject(cwd)) return;
|
|
166
|
-
refreshingBeads = true;
|
|
167
|
-
try {
|
|
168
|
-
const inProgressResult = await SubprocessRunner.run("bd", ["list", "--status=in_progress"], { cwd });
|
|
169
|
-
const inProgressRaw = inProgressResult.code === 0 ? inProgressResult.stdout : "";
|
|
170
|
-
const ids = [...new Set([...inProgressRaw.matchAll(/^◐\s+([a-z][\w-]+)/gm)].map((m) => m[1]).filter((id) => id.includes("-")))];
|
|
171
|
-
|
|
172
|
-
let claims: BeadClaim[] = [];
|
|
173
|
-
if (ids.length === 1) {
|
|
174
|
-
const [id] = ids;
|
|
175
|
-
const showResult = await SubprocessRunner.run("bd", ["show", id, "--json"], { cwd });
|
|
176
|
-
if (showResult.code === 0) {
|
|
177
|
-
try {
|
|
178
|
-
const issue = JSON.parse(showResult.stdout)?.[0];
|
|
179
|
-
claims = [{ id, shortId: getShortId(id), title: issue?.title ?? null, status: issue?.status ?? "in_progress" }];
|
|
180
|
-
} catch {
|
|
181
|
-
claims = [{ id, shortId: getShortId(id), title: null, status: "in_progress" }];
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
claims = [{ id, shortId: getShortId(id), title: null, status: "in_progress" }];
|
|
185
|
-
}
|
|
186
|
-
} else if (ids.length > 1) {
|
|
187
|
-
claims = ids.map((id) => ({ id, shortId: getShortId(id), title: null, status: "in_progress" }));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let openCount = 0;
|
|
191
|
-
if (claims.length === 0) {
|
|
192
|
-
const listResult = await SubprocessRunner.run("bd", ["list"], { cwd });
|
|
193
|
-
if (listResult.code === 0) {
|
|
194
|
-
const m = listResult.stdout.match(/\((\d+)\s+open/);
|
|
195
|
-
if (m) openCount = parseInt(m[1], 10);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
beadState = {
|
|
200
|
-
claims,
|
|
201
|
-
openCount,
|
|
202
|
-
lastFetch: Date.now(),
|
|
203
|
-
};
|
|
204
|
-
requestRender?.();
|
|
205
|
-
} catch {
|
|
206
|
-
// Fail soft — keep last known beads state.
|
|
207
|
-
} finally {
|
|
208
|
-
refreshingBeads = false;
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Build beads line: ◐ 4843.5 Rework project bootstrap... or ○ 6 open
|
|
214
|
-
*/
|
|
215
|
-
const buildBeadsLine = (width: number, theme: any): string => {
|
|
216
|
-
const { claims, openCount } = beadState;
|
|
217
|
-
|
|
218
|
-
if (claims.length === 1) {
|
|
219
|
-
const [{ shortId, title, status }] = claims;
|
|
220
|
-
const icon = STATUS_ICONS[status] ?? "◐";
|
|
221
|
-
const idChip = chip(`${icon} ${shortId}`, STATUS_BG[status] ?? CHIP_BG_NEUTRAL);
|
|
222
|
-
const cappedTitle = title ? (title.length > 40 ? `${title.slice(0, 39)}…` : title) : "";
|
|
223
|
-
const line = cappedTitle ? `${idChip} ${theme.fg("muted", cappedTitle)}` : idChip;
|
|
224
|
-
return truncateToWidth(line, width);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (claims.length > 1) {
|
|
228
|
-
const chips = claims.map(({ shortId, status }) => {
|
|
229
|
-
const icon = STATUS_ICONS[status] ?? "◐";
|
|
230
|
-
return chip(`${icon} ${shortId}`, STATUS_BG[status] ?? CHIP_BG_ACTIVE);
|
|
231
|
-
});
|
|
232
|
-
return truncateToWidth(chips.join(" "), width);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (openCount > 0) {
|
|
236
|
-
return truncateToWidth(`○ ${openCount} open`, width);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return truncateToWidth(`○ no open issues`, width);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
let footerReapplyTimer: ReturnType<typeof setTimeout> | null = null;
|
|
243
|
-
|
|
244
|
-
const applyCustomFooter = (ctx: any) => {
|
|
245
|
-
capturedCtx = ctx;
|
|
246
|
-
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
247
|
-
requestRender = () => tui.requestRender();
|
|
248
|
-
const unsub = footerData.onBranchChange(() => {
|
|
249
|
-
runtimeState.lastFetch = 0;
|
|
250
|
-
tui.requestRender();
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
dispose() {
|
|
255
|
-
unsub();
|
|
256
|
-
requestRender = null;
|
|
257
|
-
},
|
|
258
|
-
invalidate() {},
|
|
259
|
-
render(width: number): string[] {
|
|
260
|
-
refreshRuntimeState().catch(() => {});
|
|
261
|
-
refreshBeadState().catch(() => {});
|
|
262
|
-
|
|
263
|
-
// === LINE 1: ~/path (branch *+↑) ===
|
|
264
|
-
// Like original, no session name, but with git status flags
|
|
265
|
-
let pwd = process.cwd();
|
|
266
|
-
const home = process.env.HOME || process.env.USERPROFILE;
|
|
267
|
-
if (home && pwd.startsWith(home)) {
|
|
268
|
-
pwd = `~${pwd.slice(home.length)}`;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Use runtimeState branch (with git status) or fallback to footerData
|
|
272
|
-
const branch = runtimeState.branch || footerData.getGitBranch();
|
|
273
|
-
if (branch) {
|
|
274
|
-
const branchWithStatus = runtimeState.gitStatus
|
|
275
|
-
? `${branch} ${runtimeState.gitStatus}`
|
|
276
|
-
: branch;
|
|
277
|
-
pwd = `${pwd} (${branchWithStatus})`;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const pwdLine = truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "..."));
|
|
281
|
-
|
|
282
|
-
// === LINE 2: XX%/window (provider) model • thinking ===
|
|
283
|
-
const usage = ctx.getContextUsage();
|
|
284
|
-
const model = ctx.model;
|
|
285
|
-
|
|
286
|
-
const contextWindow = usage?.contextWindow ?? model?.contextWindow ?? 0;
|
|
287
|
-
const contextPercentValue = usage?.percent ?? 0;
|
|
288
|
-
const contextPercent = usage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
|
|
289
|
-
|
|
290
|
-
// Build left side: context %/window (color-coded like original)
|
|
291
|
-
const contextDisplay =
|
|
292
|
-
contextPercent === "?"
|
|
293
|
-
? `?/${formatTokens(contextWindow)}`
|
|
294
|
-
: `${contextPercent}%/${formatTokens(contextWindow)}`;
|
|
295
|
-
|
|
296
|
-
const colorizeUsage = (text: string) => {
|
|
297
|
-
if (contextPercentValue > 90) return theme.fg("error", text);
|
|
298
|
-
if (contextPercentValue > 70) return theme.fg("warning", text);
|
|
299
|
-
return text;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// Build right side: (provider) model • thinking
|
|
303
|
-
const modelName = model?.id || "no-model";
|
|
304
|
-
const providerCount = footerData.getAvailableProviderCount();
|
|
305
|
-
|
|
306
|
-
// Thinking level if model supports reasoning
|
|
307
|
-
let rightSideWithoutProvider = modelName;
|
|
308
|
-
if (model?.reasoning) {
|
|
309
|
-
const thinkingLevel = capturedPi.getThinkingLevel() || "off";
|
|
310
|
-
rightSideWithoutProvider =
|
|
311
|
-
thinkingLevel === "off" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Prepend provider if >1
|
|
315
|
-
let rightSide = rightSideWithoutProvider;
|
|
316
|
-
if (providerCount > 1 && model) {
|
|
317
|
-
rightSide = `(${model.provider}) ${rightSideWithoutProvider}`;
|
|
318
|
-
if (visibleWidth(contextDisplay) + 3 + visibleWidth(rightSide) > width) {
|
|
319
|
-
rightSide = rightSideWithoutProvider;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Keep provider/model adjacent to usage (no right-bound alignment)
|
|
324
|
-
const separator = " ";
|
|
325
|
-
const leftWidth = visibleWidth(contextDisplay);
|
|
326
|
-
const separatorWidth = visibleWidth(separator);
|
|
327
|
-
|
|
328
|
-
let line2: string;
|
|
329
|
-
if (leftWidth >= width) {
|
|
330
|
-
line2 = colorizeUsage(truncateToWidth(contextDisplay, width, ""));
|
|
331
|
-
} else {
|
|
332
|
-
const availableForRight = Math.max(0, width - leftWidth - separatorWidth);
|
|
333
|
-
const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
|
|
334
|
-
line2 = `${colorizeUsage(contextDisplay)}${theme.fg("dim", separator)}${theme.fg("dim", truncatedRight)}`;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// === LINE 3: ◐ 4843.5 Rework project bootstrap... ===
|
|
338
|
-
const line3 = buildBeadsLine(width, theme);
|
|
339
|
-
|
|
340
|
-
return [pwdLine, line2, line3];
|
|
341
|
-
},
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const scheduleFooterReapply = (ctx: any, delayMs = 40) => {
|
|
347
|
-
if (footerReapplyTimer) clearTimeout(footerReapplyTimer);
|
|
348
|
-
footerReapplyTimer = setTimeout(() => {
|
|
349
|
-
applyCustomFooter(ctx);
|
|
350
|
-
footerReapplyTimer = null;
|
|
351
|
-
}, delayMs);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
355
|
-
capturedCtx = ctx;
|
|
356
|
-
runtimeState.lastFetch = 0;
|
|
357
|
-
beadState.lastFetch = 0;
|
|
358
|
-
applyCustomFooter(ctx);
|
|
359
|
-
scheduleFooterReapply(ctx);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
363
|
-
runtimeState.lastFetch = 0;
|
|
364
|
-
scheduleFooterReapply(ctx);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
pi.on("session_fork", async (_event, ctx) => {
|
|
368
|
-
runtimeState.lastFetch = 0;
|
|
369
|
-
scheduleFooterReapply(ctx);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
pi.on("model_select", async (_event, ctx) => {
|
|
373
|
-
scheduleFooterReapply(ctx);
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
pi.on("session_shutdown", async () => {
|
|
377
|
-
if (footerReapplyTimer) {
|
|
378
|
-
clearTimeout(footerReapplyTimer);
|
|
379
|
-
footerReapplyTimer = null;
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
// Bust caches immediately after relevant writes
|
|
384
|
-
pi.on("tool_result", async (event: any) => {
|
|
385
|
-
const cmd = event?.input?.command;
|
|
386
|
-
if (!cmd) return undefined;
|
|
387
|
-
|
|
388
|
-
if (/\bbd\s+(close|update|create|claim)\b/.test(cmd)) {
|
|
389
|
-
beadState.lastFetch = 0;
|
|
390
|
-
setTimeout(() => refreshBeadState().catch(() => {}), 200);
|
|
391
|
-
}
|
|
392
|
-
if (/\bgit\s+/.test(cmd)) {
|
|
393
|
-
runtimeState.lastFetch = 0;
|
|
394
|
-
setTimeout(() => refreshRuntimeState().catch(() => {}), 200);
|
|
395
|
-
}
|
|
396
|
-
return undefined;
|
|
397
|
-
});
|
|
398
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@xtrm/pi-custom-footer",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "xtrm Pi extension: custom-footer",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": "./index.ts"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"pi",
|
|
11
|
-
"extension",
|
|
12
|
-
"xtrm"
|
|
13
|
-
],
|
|
14
|
-
"author": "xtrm",
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@xtrm/pi-core": "^1.0.0"
|
|
18
|
-
}
|
|
19
|
-
}
|