@dotdrelle/wiki-manager 0.15.34 → 0.15.38
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/.env.example +8 -5
- package/README.md +42 -0
- package/agents.docker-compose.yml +11 -16
- package/docker-compose.yml +9 -8
- package/package.json +2 -2
- package/src/agent/graph.js +20 -0
- package/src/cli/wiki-manager.js +20 -0
- package/src/commands/slash.js +85 -13
- package/src/commands/slash.test.js +85 -3
- package/src/core/agentsCompose.js +86 -1
- package/src/core/buildInfo.json +2 -2
- package/src/core/compose.js +24 -2
- package/src/core/dockerCompose.test.js +18 -0
- package/src/core/env.js +2 -1
- package/src/core/env.test.js +14 -0
- package/src/core/googleGrants.js +38 -0
- package/src/core/googleGrants.test.js +59 -0
- package/src/core/mcp.js +1 -1
- package/src/core/mcp.test.js +1 -0
- package/src/core/mcpEndpoints.js +96 -0
- package/src/core/mcpEndpoints.test.js +126 -0
- package/src/core/modelFetch.js +241 -27
- package/src/core/modelFetch.test.js +213 -0
- package/src/core/profileServiceStatus.test.js +146 -0
- package/src/core/wikiSetup.js +11 -2
- package/src/core/wikiWorkspace.test.js +16 -3
- package/src/orchestrator/agentRegistry.js +13 -1
- package/src/orchestrator/agentRegistry.test.js +20 -0
- package/src/runtime/lifecycle.js +105 -3
- package/src/runtime/lifecycle.test.js +68 -0
- package/src/runtime/server.js +28 -0
- package/src/shell/LeftPane.tsx +55 -41
- package/src/shell/SetupWizard.tsx +416 -116
- package/src/shell/repl.js +61 -12
- package/src/shell/repl.test.js +48 -19
- package/src/shell/setupWizardDiscovery.test.js +48 -0
- package/src/shell/setupWizardPlaceholders.test.js +15 -2
- package/src/shell/setupWizardSuggestions.test.js +36 -2
- package/src/shell/wrapText.js +57 -0
- package/src/shell/wrapText.test.js +48 -0
- package/wiki-workspace +122 -32
package/src/shell/LeftPane.tsx
CHANGED
|
@@ -139,20 +139,52 @@ function WelcomeHelpPanels(props: { width: number }) {
|
|
|
139
139
|
);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const COPY_BTN = '
|
|
142
|
+
const COPY_BTN = ' copy';
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Barre de gouttière, préfixée à chaque ligne d'un message.
|
|
146
|
+
*
|
|
147
|
+
* Remplace le filet horizontal pleine largeur qui ouvrait chaque message.
|
|
148
|
+
* Celui-ci était l'élément le plus encré de l'écran, répété à chaque tour, et
|
|
149
|
+
* il séparait là où il fallait grouper : rien n'indiquait où un bloc finissait,
|
|
150
|
+
* seulement où le suivant commençait. Une barre colorée porte la même
|
|
151
|
+
* information — à qui est le tour, où le bloc commence ET où il s'arrête — pour
|
|
152
|
+
* un caractère par ligne au lieu de toute la largeur.
|
|
153
|
+
*/
|
|
154
|
+
const GUTTER = '▌ ';
|
|
155
|
+
|
|
156
|
+
function clockLabel(at?: number): string {
|
|
157
|
+
if (!Number.isFinite(at)) return '';
|
|
158
|
+
const date = new Date(at as number);
|
|
159
|
+
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
|
160
|
+
}
|
|
143
161
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
/**
|
|
163
|
+
* En-tête d'un message : gouttière, locuteur, heure, et `copy` à droite.
|
|
164
|
+
*
|
|
165
|
+
* Le bouton de copie était collé au libellé, précédé d'un filet de soixante-dix
|
|
166
|
+
* tirets. Poussé au bord droit et dégrisé, il reste atteignable sans peser sur
|
|
167
|
+
* la ligne. L'heure était reléguée dans une colonne latérale, détachée de ce
|
|
168
|
+
* qu'elle datait ; elle coûte cinq caractères ici et devient lisible.
|
|
169
|
+
*/
|
|
170
|
+
function messageHeaderSegments(role: string, columns: number, at?: number): Segment[] {
|
|
171
|
+
const time = clockLabel(at);
|
|
172
|
+
const label = roleLabel(role);
|
|
173
|
+
const used = GUTTER.length + label.length + (time ? time.length + 2 : 0);
|
|
174
|
+
const pad = Math.max(1, columns - used - COPY_BTN.length);
|
|
149
175
|
return [
|
|
150
|
-
{ text:
|
|
176
|
+
{ text: GUTTER, color: roleColor(role) },
|
|
151
177
|
{ text: label, color: roleColor(role) },
|
|
152
|
-
{ text:
|
|
178
|
+
...(time ? [{ text: ` ${time}`, color: '#4B5563' }] : []),
|
|
179
|
+
{ text: ' '.repeat(pad), color: '#4B5563' },
|
|
153
180
|
];
|
|
154
181
|
}
|
|
155
182
|
|
|
183
|
+
/** Préfixe la gouttière à une ligne de corps de message. */
|
|
184
|
+
function withGutter(line: RenderedLine, role: string): RenderedLine {
|
|
185
|
+
return { ...line, segments: [{ text: GUTTER, color: roleColor(role) }, ...line.segments] };
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
// Split " /cmd [<arg>...] description" into two segments.
|
|
157
189
|
// Uses non-greedy to stop at the first double-space separator.
|
|
158
190
|
function splitCmdDesc(line: string): [string, string] | null {
|
|
@@ -413,21 +445,24 @@ function headerGroupKey(role: string) {
|
|
|
413
445
|
return isDonnaRole(role) ? 'donna' : roleLabel(role);
|
|
414
446
|
}
|
|
415
447
|
|
|
416
|
-
function conversationLines(messages: Array<{ role: string; content: string }>, columns: number): RenderedLine[] {
|
|
448
|
+
function conversationLines(messages: Array<{ role: string; content: string; at?: number }>, columns: number): RenderedLine[] {
|
|
417
449
|
return messages.flatMap((message, index) => {
|
|
418
450
|
const raw = String(message.content || '');
|
|
419
451
|
const previous = index > 0 ? messages[index - 1] : null;
|
|
420
452
|
const showHeader = !previous || headerGroupKey(previous.role) !== headerGroupKey(message.role);
|
|
453
|
+
// Plus de ligne vide sous l'en-tête : le filet et son rembourrage
|
|
454
|
+
// coûtaient deux lignes par message, en plus de la ligne de séparation.
|
|
455
|
+
// La gouttière rattache visuellement l'en-tête à son corps, la respiration
|
|
456
|
+
// entre tours suffit.
|
|
421
457
|
const headerLines: RenderedLine[] = showHeader
|
|
422
|
-
? [
|
|
423
|
-
{
|
|
424
|
-
segments: messageHeaderSegments(message.role, columns),
|
|
425
|
-
copyContent: raw,
|
|
426
|
-
},
|
|
427
|
-
{ segments: [{ text: ' ', color: '#D6DEE8' }] },
|
|
428
|
-
]
|
|
458
|
+
? [{ segments: messageHeaderSegments(message.role, columns, message.at), copyContent: raw }]
|
|
429
459
|
: [];
|
|
430
|
-
|
|
460
|
+
// Les messages utilisateur étaient repliés à 60 % de la largeur pour tenir
|
|
461
|
+
// dans une bulle alignée à droite. Cela coupait les phrases en plein milieu
|
|
462
|
+
// et faisait repartir l'œil de deux colonnes différentes selon le locuteur.
|
|
463
|
+
// La couleur de gouttière distingue les tours ; la colonne de lecture reste
|
|
464
|
+
// unique. On retire aussi la largeur de la gouttière du texte disponible.
|
|
465
|
+
const contentColumns = Math.max(12, columns - GUTTER.length);
|
|
431
466
|
if (isStatusOutput(message)) {
|
|
432
467
|
const statusLines: RenderedLine[] = [
|
|
433
468
|
...headerLines,
|
|
@@ -454,15 +489,9 @@ function conversationLines(messages: Array<{ role: string; content: string }>, c
|
|
|
454
489
|
lines.push({ text: line, isCode: inFence });
|
|
455
490
|
}
|
|
456
491
|
const renderedBody = renderMarkdownLines(lines, message.role, contentColumns);
|
|
457
|
-
const userBubbleWidth = message.role === 'user'
|
|
458
|
-
? Math.min(
|
|
459
|
-
Math.max(1, ...renderedBody.map((line) => line.segments.reduce((sum, segment) => sum + segment.text.length, 0))) + 2,
|
|
460
|
-
Math.max(12, Math.floor(columns * 0.6)),
|
|
461
|
-
)
|
|
462
|
-
: undefined;
|
|
463
492
|
const bodyLines: RenderedLine[] = [
|
|
464
493
|
...headerLines,
|
|
465
|
-
...renderedBody.map((line) => (
|
|
494
|
+
...renderedBody.map((line) => withGutter(line, message.role)),
|
|
466
495
|
{ segments: [{ text: ' ', color: '#D6DEE8' }] },
|
|
467
496
|
];
|
|
468
497
|
return bodyLines.map((line) => ({ ...line, role: message.role }));
|
|
@@ -470,7 +499,7 @@ function conversationLines(messages: Array<{ role: string; content: string }>, c
|
|
|
470
499
|
}
|
|
471
500
|
|
|
472
501
|
export function ConversationView(props: {
|
|
473
|
-
messages: Array<{ role: string; content: string }>;
|
|
502
|
+
messages: Array<{ role: string; content: string; at?: number }>;
|
|
474
503
|
rows: number;
|
|
475
504
|
columns: number;
|
|
476
505
|
scroll: number;
|
|
@@ -536,21 +565,6 @@ export function ConversationView(props: {
|
|
|
536
565
|
</For>
|
|
537
566
|
<text fg="#4B5563" content={COPY_BTN} onMouseUp={() => props.onCopy?.(line.copyContent!)} />
|
|
538
567
|
</box>
|
|
539
|
-
) : line.role === 'user' && line.userBubbleWidth ? (
|
|
540
|
-
<box height={1} flexDirection="row" overflow="hidden">
|
|
541
|
-
<box flexGrow={1} />
|
|
542
|
-
<box
|
|
543
|
-
width={line.userBubbleWidth}
|
|
544
|
-
height={1}
|
|
545
|
-
flexDirection="row"
|
|
546
|
-
flexShrink={1}
|
|
547
|
-
justifyContent="flex-start"
|
|
548
|
-
paddingX={1}
|
|
549
|
-
overflow="hidden"
|
|
550
|
-
>
|
|
551
|
-
<text>{styledSegments(line.segments)}</text>
|
|
552
|
-
</box>
|
|
553
|
-
</box>
|
|
554
568
|
) : line.status ? (
|
|
555
569
|
<box height={1} flexDirection="row" gap={2} overflow="hidden">
|
|
556
570
|
<box
|
|
@@ -786,7 +800,7 @@ export function LeftPane(props: {
|
|
|
786
800
|
statusLine: string;
|
|
787
801
|
hintLine?: string | null;
|
|
788
802
|
showWelcome: boolean;
|
|
789
|
-
messages: Array<{ role: string; content: string }>;
|
|
803
|
+
messages: Array<{ role: string; content: string; at?: number }>;
|
|
790
804
|
prompt: string;
|
|
791
805
|
input: string;
|
|
792
806
|
busy: boolean;
|