@thoth-agents/pi-thoth-theme 0.0.0-stage → 0.1.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/LICENSE +29 -0
- package/README.md +115 -2
- package/package.json +63 -3
- package/src/index.ts +53 -0
- package/src/input-box/decorate.ts +304 -0
- package/src/input-box/frame.ts +182 -0
- package/src/input-box/gradient.ts +92 -0
- package/src/input-box/state.ts +90 -0
- package/src/render-kit/index.ts +305 -0
- package/src/render-kit/working.ts +25 -0
- package/src/shared/cache.ts +21 -0
- package/src/shared/config.ts +86 -0
- package/src/shared/duration.ts +1 -0
- package/src/shared/icons.ts +89 -0
- package/src/status-line/cost.ts +56 -0
- package/src/status-line/index.ts +242 -0
- package/src/status-line/layout.ts +272 -0
- package/src/status-line/snapshot.ts +11 -0
- package/src/status-line/throughput.ts +61 -0
- package/src/status-line/tokens.ts +51 -0
- package/src/tools/bash.ts +237 -0
- package/src/tools/border.ts +21 -0
- package/src/tools/box.ts +258 -0
- package/src/tools/edit.ts +228 -0
- package/src/tools/file-icons.ts +139 -0
- package/src/tools/find.ts +227 -0
- package/src/tools/frame.ts +166 -0
- package/src/tools/generic.ts +195 -0
- package/src/tools/grep.ts +371 -0
- package/src/tools/index.ts +135 -0
- package/src/tools/ls.ts +231 -0
- package/src/tools/ownership.ts +70 -0
- package/src/tools/powershell.ts +1 -0
- package/src/tools/read.ts +194 -0
- package/src/tools/ticker.ts +101 -0
- package/src/tools/write.ts +188 -0
- package/src/welcome/age.ts +11 -0
- package/src/welcome/index.ts +132 -0
- package/src/welcome/logo.ts +35 -0
- package/src/welcome/render.ts +323 -0
- package/src/welcome/resources.ts +251 -0
- package/themes/thoth.json +104 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
SourceInfo,
|
|
5
|
+
ToolInfo,
|
|
6
|
+
} from '@earendil-works/pi-coding-agent';
|
|
7
|
+
import {
|
|
8
|
+
VERSION as PI_VERSION,
|
|
9
|
+
SessionManager,
|
|
10
|
+
} from '@earendil-works/pi-coding-agent';
|
|
11
|
+
import type { IconMode } from '../shared/config.ts';
|
|
12
|
+
import { icon } from '../shared/icons.ts';
|
|
13
|
+
import { formatAge } from './age.ts';
|
|
14
|
+
|
|
15
|
+
export interface WelcomeProvider {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly detail: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WelcomeSession {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly timeAgo: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface WelcomeResourceCounts {
|
|
26
|
+
readonly tools?: number | undefined;
|
|
27
|
+
readonly skills?: number | undefined;
|
|
28
|
+
readonly extensions?: number | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WelcomeData {
|
|
32
|
+
readonly version: string;
|
|
33
|
+
readonly model?: string | undefined;
|
|
34
|
+
readonly provider?: string | undefined;
|
|
35
|
+
readonly resources: WelcomeResourceCounts;
|
|
36
|
+
readonly providers: readonly WelcomeProvider[];
|
|
37
|
+
readonly sessions: readonly WelcomeSession[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ResourceLoaderLike {
|
|
41
|
+
getSkills?(): { skills: unknown[] };
|
|
42
|
+
getExtensions?(): { extensions: unknown[] };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface StartupResourceOptions {
|
|
46
|
+
sessionLister?: (cwd: string) => Promise<WelcomeSession[]>;
|
|
47
|
+
skillCountLister?: (cwd: string) => number;
|
|
48
|
+
extensionCountLister?: (cwd: string) => number;
|
|
49
|
+
resourceLoader?: ResourceLoaderLike;
|
|
50
|
+
iconMode?: IconMode;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toolSourceLabel(sourceInfo: SourceInfo | undefined): string {
|
|
54
|
+
if (!sourceInfo || typeof sourceInfo !== 'object') return 'core';
|
|
55
|
+
const source = typeof sourceInfo.source === 'string' ? sourceInfo.source : '';
|
|
56
|
+
if (source === 'builtin' || source === 'core') return 'core';
|
|
57
|
+
if (source === 'sdk') return 'sdk';
|
|
58
|
+
if (source.startsWith('npm:')) return source.slice('npm:'.length) || source;
|
|
59
|
+
if (source.startsWith('git:')) return source.replace(/\.git(?:#.*)?$/i, '');
|
|
60
|
+
const baseDir =
|
|
61
|
+
typeof sourceInfo.baseDir === 'string' ? sourceInfo.baseDir : '';
|
|
62
|
+
if (baseDir) {
|
|
63
|
+
const parts = baseDir.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
64
|
+
return parts[parts.length - 1] || 'extension';
|
|
65
|
+
}
|
|
66
|
+
const path = typeof sourceInfo.path === 'string' ? sourceInfo.path : '';
|
|
67
|
+
if (path) {
|
|
68
|
+
const parts = path.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
69
|
+
const last = parts[parts.length - 1] || 'extension';
|
|
70
|
+
return last.replace(/\.(?:ts|js|mjs|cjs)$/i, '');
|
|
71
|
+
}
|
|
72
|
+
return source || 'extension';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function groupToolDetails(tools: readonly ToolInfo[]): WelcomeProvider[] {
|
|
76
|
+
const groups = new Map<string, string[]>();
|
|
77
|
+
for (const tool of tools) {
|
|
78
|
+
if (!tool || typeof tool.name !== 'string') continue;
|
|
79
|
+
const name = tool.name.trim();
|
|
80
|
+
if (!name) continue;
|
|
81
|
+
const label = toolSourceLabel(tool.sourceInfo);
|
|
82
|
+
let list = groups.get(label);
|
|
83
|
+
if (!list) {
|
|
84
|
+
list = [];
|
|
85
|
+
groups.set(label, list);
|
|
86
|
+
}
|
|
87
|
+
list.push(name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result: WelcomeProvider[] = [];
|
|
91
|
+
const sortedKeys = Array.from(groups.keys()).sort((a, b) => {
|
|
92
|
+
if (a === 'core') return -1;
|
|
93
|
+
if (b === 'core') return 1;
|
|
94
|
+
return a.localeCompare(b);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
for (const key of sortedKeys) {
|
|
98
|
+
const toolNames = (groups.get(key) || []).sort();
|
|
99
|
+
const sample = toolNames.slice(0, 3).join(' ');
|
|
100
|
+
const remaining = toolNames.length > 3 ? ` +${toolNames.length - 3}` : '';
|
|
101
|
+
result.push({
|
|
102
|
+
name: key,
|
|
103
|
+
detail: `${sample}${remaining}`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result.slice(0, 4);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function fetchRecentSessions(
|
|
111
|
+
cwd: string,
|
|
112
|
+
limit = 4,
|
|
113
|
+
mode: IconMode = 'nerd',
|
|
114
|
+
): Promise<WelcomeSession[]> {
|
|
115
|
+
try {
|
|
116
|
+
const sessions = await SessionManager.list(cwd);
|
|
117
|
+
const sorted = [...sessions].sort(
|
|
118
|
+
(a, b) => (b.modified?.getTime?.() ?? 0) - (a.modified?.getTime?.() ?? 0),
|
|
119
|
+
);
|
|
120
|
+
const out: WelcomeSession[] = [];
|
|
121
|
+
for (const session of sorted) {
|
|
122
|
+
if (out.length >= limit) break;
|
|
123
|
+
const rawTitle =
|
|
124
|
+
session.name?.trim() ||
|
|
125
|
+
session.firstMessage?.trim() ||
|
|
126
|
+
'Untitled session';
|
|
127
|
+
const cleanTitle = Array.from(rawTitle)
|
|
128
|
+
.map((c) => {
|
|
129
|
+
const code = c.charCodeAt(0);
|
|
130
|
+
return code < 32 || code === 127 ? ' ' : c;
|
|
131
|
+
})
|
|
132
|
+
.join('')
|
|
133
|
+
.replace(/\s+/g, ' ')
|
|
134
|
+
.trim();
|
|
135
|
+
const title =
|
|
136
|
+
cleanTitle.length > 45
|
|
137
|
+
? `${cleanTitle.slice(0, 44)}${icon('ellipsis', mode)}`
|
|
138
|
+
: cleanTitle;
|
|
139
|
+
const timeAgo = formatAge(
|
|
140
|
+
session.modified ? session.modified.getTime() : Date.now(),
|
|
141
|
+
);
|
|
142
|
+
out.push({ name: title, timeAgo });
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
} catch {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function collectStartupResources(
|
|
151
|
+
pi: ExtensionAPI,
|
|
152
|
+
ctx: ExtensionContext,
|
|
153
|
+
options?: StartupResourceOptions,
|
|
154
|
+
): Promise<WelcomeData> {
|
|
155
|
+
let tools: ToolInfo[] = [];
|
|
156
|
+
let toolsCount: number | undefined;
|
|
157
|
+
|
|
158
|
+
if (typeof pi.getAllTools === 'function') {
|
|
159
|
+
try {
|
|
160
|
+
tools = pi.getAllTools() ?? [];
|
|
161
|
+
toolsCount = tools.length;
|
|
162
|
+
} catch {
|
|
163
|
+
// ignore
|
|
164
|
+
}
|
|
165
|
+
} else if (typeof pi.getActiveTools === 'function') {
|
|
166
|
+
try {
|
|
167
|
+
const active = pi.getActiveTools() ?? [];
|
|
168
|
+
toolsCount = active.length;
|
|
169
|
+
} catch {
|
|
170
|
+
// ignore
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const providers = groupToolDetails(tools);
|
|
175
|
+
|
|
176
|
+
const loader: ResourceLoaderLike | undefined =
|
|
177
|
+
options?.resourceLoader ??
|
|
178
|
+
(ctx as unknown as { resourceLoader?: ResourceLoaderLike })
|
|
179
|
+
?.resourceLoader ??
|
|
180
|
+
(pi as unknown as { resourceLoader?: ResourceLoaderLike })
|
|
181
|
+
?.resourceLoader ??
|
|
182
|
+
(ctx as unknown as { session?: { resourceLoader?: ResourceLoaderLike } })
|
|
183
|
+
?.session?.resourceLoader;
|
|
184
|
+
|
|
185
|
+
// 1. Loaded skills: live public source from options, resourceLoader, or pi.getCommands()
|
|
186
|
+
let skillsCount: number | undefined;
|
|
187
|
+
if (options?.skillCountLister) {
|
|
188
|
+
skillsCount = options.skillCountLister(ctx.cwd);
|
|
189
|
+
} else if (loader?.getSkills) {
|
|
190
|
+
try {
|
|
191
|
+
const skillsRes = loader.getSkills();
|
|
192
|
+
if (skillsRes && Array.isArray(skillsRes.skills)) {
|
|
193
|
+
skillsCount = skillsRes.skills.length;
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// ignore
|
|
197
|
+
}
|
|
198
|
+
} else if (typeof pi.getCommands === 'function') {
|
|
199
|
+
try {
|
|
200
|
+
const commands = pi.getCommands();
|
|
201
|
+
if (Array.isArray(commands)) {
|
|
202
|
+
const skillCommands = commands.filter(
|
|
203
|
+
(c) =>
|
|
204
|
+
c?.source === 'skill' ||
|
|
205
|
+
(typeof c?.name === 'string' && c.name.startsWith('skill:')),
|
|
206
|
+
);
|
|
207
|
+
skillsCount = skillCommands.length;
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// ignore
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 2. Loaded extensions: live public source from options or resourceLoader
|
|
215
|
+
let extensionsCount: number | undefined;
|
|
216
|
+
if (options?.extensionCountLister) {
|
|
217
|
+
extensionsCount = options.extensionCountLister(ctx.cwd);
|
|
218
|
+
} else if (loader?.getExtensions) {
|
|
219
|
+
try {
|
|
220
|
+
const extRes = loader.getExtensions();
|
|
221
|
+
if (extRes && Array.isArray(extRes.extensions)) {
|
|
222
|
+
extensionsCount = extRes.extensions.length;
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
// ignore
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let sessions: WelcomeSession[] = [];
|
|
230
|
+
if (options?.sessionLister) {
|
|
231
|
+
sessions = await options.sessionLister(ctx.cwd);
|
|
232
|
+
} else {
|
|
233
|
+
sessions = await fetchRecentSessions(ctx.cwd, 4, options?.iconMode);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const modelName = ctx.model?.name || ctx.model?.id;
|
|
237
|
+
const provider = ctx.model?.provider;
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
version: PI_VERSION,
|
|
241
|
+
model: modelName,
|
|
242
|
+
provider,
|
|
243
|
+
resources: {
|
|
244
|
+
tools: toolsCount,
|
|
245
|
+
skills: skillsCount,
|
|
246
|
+
extensions: extensionsCount,
|
|
247
|
+
},
|
|
248
|
+
providers,
|
|
249
|
+
sessions,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
3
|
+
"name": "thoth",
|
|
4
|
+
"appearance": "dark",
|
|
5
|
+
"vars": {
|
|
6
|
+
"black": "#000000",
|
|
7
|
+
"obsidian": "#0D0B08",
|
|
8
|
+
"gold": "#D4AF37",
|
|
9
|
+
"brightGold": "#F2C94C",
|
|
10
|
+
"papyrus": "#E8DCB5",
|
|
11
|
+
"sand": "#A89A78",
|
|
12
|
+
"dimSand": "#736850",
|
|
13
|
+
"shadowSand": "#4A4232",
|
|
14
|
+
"darkSand": "#332D22",
|
|
15
|
+
"lapis": "#2E5EAA",
|
|
16
|
+
"lightLapis": "#5B8DEF",
|
|
17
|
+
"deepLapis": "#17325C",
|
|
18
|
+
"darkLapisInfo": "#101A26",
|
|
19
|
+
"turquoise": "#3FB8AF",
|
|
20
|
+
"malachite": "#6CBF5A",
|
|
21
|
+
"carnelian": "#C0503A",
|
|
22
|
+
"red": "#E05A4F",
|
|
23
|
+
"ochre": "#D98E04",
|
|
24
|
+
"amber": "#E0A030",
|
|
25
|
+
"bronze": "#5C4A32",
|
|
26
|
+
"darkBronze": "#3A2E1F",
|
|
27
|
+
"subtleBronze": "#241D14",
|
|
28
|
+
"userMsgBg": "#120E0A",
|
|
29
|
+
"customMsgBg": "#16120C",
|
|
30
|
+
"toolPendingBg": "#14100A",
|
|
31
|
+
"toolSuccessBg": "#08160A",
|
|
32
|
+
"toolErrorBg": "#1A0808",
|
|
33
|
+
"thinkingOff": "#3A3326",
|
|
34
|
+
"thinkingMinimal": "#544A37",
|
|
35
|
+
"thinkingLow": "#786B4F",
|
|
36
|
+
"thinkingMedium": "#A89A78",
|
|
37
|
+
"thinkingHigh": "#D4AF37",
|
|
38
|
+
"thinkingXhigh": "#F2C94C",
|
|
39
|
+
"thinkingMax": "#C0503A"
|
|
40
|
+
},
|
|
41
|
+
"colors": {
|
|
42
|
+
"accent": "gold",
|
|
43
|
+
"border": "bronze",
|
|
44
|
+
"borderAccent": "gold",
|
|
45
|
+
"borderMuted": "darkBronze",
|
|
46
|
+
"success": "malachite",
|
|
47
|
+
"error": "red",
|
|
48
|
+
"warning": "amber",
|
|
49
|
+
"muted": "sand",
|
|
50
|
+
"dim": "dimSand",
|
|
51
|
+
"text": "papyrus",
|
|
52
|
+
"thinkingText": "sand",
|
|
53
|
+
"scrollbarTrack": "darkBronze",
|
|
54
|
+
"scrollbarThumb": "bronze",
|
|
55
|
+
"selectedBg": "deepLapis",
|
|
56
|
+
"searchMatchBg": "lapis",
|
|
57
|
+
"searchMatchText": "papyrus",
|
|
58
|
+
"userMessageBg": "userMsgBg",
|
|
59
|
+
"userMessageText": "papyrus",
|
|
60
|
+
"customMessageBg": "customMsgBg",
|
|
61
|
+
"customMessageText": "sand",
|
|
62
|
+
"customMessageLabel": "brightGold",
|
|
63
|
+
"toolPendingBg": "toolPendingBg",
|
|
64
|
+
"toolSuccessBg": "toolSuccessBg",
|
|
65
|
+
"toolErrorBg": "toolErrorBg",
|
|
66
|
+
"toolTitle": "brightGold",
|
|
67
|
+
"toolOutput": "sand",
|
|
68
|
+
"mdHeading": "brightGold",
|
|
69
|
+
"mdLink": "lightLapis",
|
|
70
|
+
"mdLinkUrl": "lapis",
|
|
71
|
+
"mdCode": "turquoise",
|
|
72
|
+
"mdCodeBlock": "papyrus",
|
|
73
|
+
"mdCodeBlockBorder": "darkBronze",
|
|
74
|
+
"mdQuote": "sand",
|
|
75
|
+
"mdQuoteBorder": "gold",
|
|
76
|
+
"mdHr": "darkBronze",
|
|
77
|
+
"mdListBullet": "gold",
|
|
78
|
+
"toolDiffAdded": "malachite",
|
|
79
|
+
"toolDiffRemoved": "red",
|
|
80
|
+
"toolDiffContext": "sand",
|
|
81
|
+
"syntaxComment": "dimSand",
|
|
82
|
+
"syntaxKeyword": "gold",
|
|
83
|
+
"syntaxFunction": "brightGold",
|
|
84
|
+
"syntaxVariable": "papyrus",
|
|
85
|
+
"syntaxString": "malachite",
|
|
86
|
+
"syntaxNumber": "ochre",
|
|
87
|
+
"syntaxType": "lightLapis",
|
|
88
|
+
"syntaxOperator": "gold",
|
|
89
|
+
"syntaxPunctuation": "sand",
|
|
90
|
+
"thinkingOff": "thinkingOff",
|
|
91
|
+
"thinkingMinimal": "thinkingMinimal",
|
|
92
|
+
"thinkingLow": "thinkingLow",
|
|
93
|
+
"thinkingMedium": "thinkingMedium",
|
|
94
|
+
"thinkingHigh": "thinkingHigh",
|
|
95
|
+
"thinkingXhigh": "thinkingXhigh",
|
|
96
|
+
"thinkingMax": "thinkingMax",
|
|
97
|
+
"bashMode": "gold"
|
|
98
|
+
},
|
|
99
|
+
"export": {
|
|
100
|
+
"pageBg": "black",
|
|
101
|
+
"cardBg": "obsidian",
|
|
102
|
+
"infoBg": "darkLapisInfo"
|
|
103
|
+
}
|
|
104
|
+
}
|