@dotdrelle/wiki-manager 0.6.31 → 0.6.34
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/README.md +3 -2
- package/bin/wiki-manager.js +1 -1
- package/package.json +8 -7
- package/src/agent/graph.js +5 -10
- package/src/cli/wiki-manager.js +17 -2
- package/src/commands/slash.js +104 -27
- package/src/commands/slash.test.js +68 -0
- package/src/core/compose.js +1 -1
- package/src/core/mcp.js +1 -1
- package/src/core/modelFetch.js +97 -0
- package/src/core/modelFetch.test.js +38 -0
- package/src/core/startupCheck.js +130 -0
- package/src/core/wikiSetup.js +156 -0
- package/src/core/wikirc.js +80 -1
- package/src/core/wikirc.test.js +111 -0
- package/src/core/workspaces.js +1 -1
- package/src/shell/LeftPane.tsx +54 -28
- package/src/shell/RightPane.tsx +25 -2
- package/src/shell/SetupWizard.tsx +806 -0
- package/src/shell/SlashDialog.tsx +4 -3
- package/src/shell/repl.js +20 -8
- package/src/shell/tui.tsx +85 -13
- package/src/shell/useSession.ts +15 -7
- package/wiki-workspace +3 -3
|
@@ -24,14 +24,15 @@ export function SlashDialog(props: { context: any }) {
|
|
|
24
24
|
{(item: any, index) => (
|
|
25
25
|
<text
|
|
26
26
|
height={1}
|
|
27
|
-
fg={index() === context().
|
|
28
|
-
bg={index() === context().
|
|
27
|
+
fg={index() === context().visibleSelected ? '#111318' : '#D6DEE8'}
|
|
28
|
+
bg={index() === context().visibleSelected ? '#8BD5CA' : '#111318'}
|
|
29
29
|
>
|
|
30
30
|
{item.value.padEnd(16, ' ')} {item.description}
|
|
31
31
|
</text>
|
|
32
32
|
)}
|
|
33
33
|
</For>
|
|
34
|
-
<text
|
|
34
|
+
<text height={1} />
|
|
35
|
+
<text fg="#7F8C8D">[up/down navigate, Tab complete, Enter confirm, Esc clear]</text>
|
|
35
36
|
</box>
|
|
36
37
|
)}
|
|
37
38
|
</Show>
|
package/src/shell/repl.js
CHANGED
|
@@ -54,8 +54,8 @@ const COMMAND_COMPLETION_DESCRIPTIONS = {
|
|
|
54
54
|
'/help': 'Show shell commands.',
|
|
55
55
|
'/version': 'Print the wiki-manager version.',
|
|
56
56
|
'/exit': 'Exit the shell.',
|
|
57
|
-
'/
|
|
58
|
-
'/new': '
|
|
57
|
+
'/workspace': 'List, create, or delete workspaces.',
|
|
58
|
+
'/new': 'Open the setup wizard in the interactive TUI.',
|
|
59
59
|
'/use': 'Load a workspace and its default config.',
|
|
60
60
|
'/config': 'Inspect or switch .wikirc.yaml profiles.',
|
|
61
61
|
'/status': 'Show the current workspace and session state.',
|
|
@@ -89,7 +89,9 @@ const SUBCOMMAND_COMPLETION_DESCRIPTIONS = {
|
|
|
89
89
|
'/queue': 'Inspect or cancel queued MCP jobs.',
|
|
90
90
|
'/queue:cancel': 'Cancel a queued or running queue item.',
|
|
91
91
|
'/queue:clear': 'Clear finished queue items.',
|
|
92
|
-
'/workspace:init': '
|
|
92
|
+
'/workspace:init': 'Low-level workspace creation.',
|
|
93
|
+
'/workspace:list': 'List configured workspaces.',
|
|
94
|
+
'/workspace:delete': 'Delete one workspace after confirmation.',
|
|
93
95
|
'/wiki:run': 'Use the low-level llm-wiki CLI fallback.',
|
|
94
96
|
'/skills:edit': 'Edit one workspace skill file.',
|
|
95
97
|
'/skills:list': 'List workspace skills.',
|
|
@@ -106,7 +108,7 @@ export function createSession() {
|
|
|
106
108
|
wikircConfig: null,
|
|
107
109
|
language: null,
|
|
108
110
|
mcp: null,
|
|
109
|
-
commands: ['help', 'version', 'exit', '
|
|
111
|
+
commands: ['help', 'version', 'exit', 'workspace', 'new', 'use', 'config', 'status', 'services', 'start', 'stop', 'logs', 'mcp', 'wiki', 'skills', 'upload', 'uploads', 'clear', 'chat', 'agent', 'openui', 'queue'],
|
|
110
112
|
chatMode: true,
|
|
111
113
|
llm: null,
|
|
112
114
|
activities: {},
|
|
@@ -216,11 +218,16 @@ function completionValuesFor(parts, inputBuffer, session) {
|
|
|
216
218
|
.filter((item) => ['waiting', 'starting', 'running'].includes(item.status))
|
|
217
219
|
.map((item) => item.id);
|
|
218
220
|
}
|
|
219
|
-
if (command === '/workspace'
|
|
221
|
+
if (command === '/workspace') {
|
|
222
|
+
if (tokenIndex === 1) return ['delete', 'init', 'list'];
|
|
223
|
+
if (previousToken === 'delete') return workspaceNames();
|
|
224
|
+
if (parts[1] === 'delete' && tokenIndex === 3) return ['--confirm'];
|
|
225
|
+
}
|
|
220
226
|
if (command === '/wiki' && tokenIndex === 1) return ['run'];
|
|
221
227
|
if (command === '/skills' && tokenIndex === 1) return ['edit', 'list', 'run', 'show'];
|
|
222
228
|
if (command === '/skills' && ['edit', 'run', 'show'].includes(previousToken ?? '')) return skillNames(session);
|
|
223
|
-
if ((command === '/start' || command === '/stop'
|
|
229
|
+
if ((command === '/start' || command === '/stop') && tokenIndex === 1) return ['agents', ...serviceNames()];
|
|
230
|
+
if (command === '/logs' && tokenIndex === 1) return serviceNames();
|
|
224
231
|
return [];
|
|
225
232
|
}
|
|
226
233
|
|
|
@@ -289,6 +296,11 @@ export function completionDescription(value, parts) {
|
|
|
289
296
|
if (command === '/start') return serviceDescription(value) ?? 'Start this Docker Compose service.';
|
|
290
297
|
if (command === '/stop') return serviceDescription(value) ?? 'Stop this Docker Compose service.';
|
|
291
298
|
if (command === '/logs') return serviceDescription(value) ?? 'Show logs for this Docker Compose service.';
|
|
299
|
+
if (command === '/workspace') {
|
|
300
|
+
if (parts[1] === 'delete' && value === '--confirm') return 'Confirm workspace deletion.';
|
|
301
|
+
if (parts.at(-1) === 'delete') return 'Delete this workspace.';
|
|
302
|
+
return 'Choose a workspace action.';
|
|
303
|
+
}
|
|
292
304
|
if (command === '/mcp') return parts[1] === 'call' ? 'Use this MCP server.' : 'Filter tools to this MCP server.';
|
|
293
305
|
if (command === '/skills') {
|
|
294
306
|
if (parts.at(-1) === 'edit') return 'Edit this skill.';
|
|
@@ -1174,7 +1186,7 @@ async function runTuiShell({ agent, packageJson, session }) {
|
|
|
1174
1186
|
rerender();
|
|
1175
1187
|
|
|
1176
1188
|
try {
|
|
1177
|
-
const { output: wsOutput } = await handleSlashCommand('/
|
|
1189
|
+
const { output: wsOutput } = await handleSlashCommand('/workspace list', { packageJson, session });
|
|
1178
1190
|
if (wsOutput) messages.push({ role: 'command', content: wsOutput });
|
|
1179
1191
|
} finally {
|
|
1180
1192
|
clearInterval(spinnerInterval);
|
|
@@ -1316,7 +1328,7 @@ async function runTuiShell({ agent, packageJson, session }) {
|
|
|
1316
1328
|
}
|
|
1317
1329
|
return;
|
|
1318
1330
|
}
|
|
1319
|
-
if (key?.ctrl && key.name === 'c') {
|
|
1331
|
+
if ((key?.ctrl || key?.meta) && key.name === 'c') {
|
|
1320
1332
|
if (busy) {
|
|
1321
1333
|
currentAbortController?.abort();
|
|
1322
1334
|
spinnerLabel = 'Interrupting…';
|
package/src/shell/tui.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { FileEditorDialog } from './FileEditorDialog';
|
|
|
6
6
|
import { LeftPane } from './LeftPane';
|
|
7
7
|
import { RightPane } from './RightPane';
|
|
8
8
|
import { SlashDialog } from './SlashDialog';
|
|
9
|
+
import { SetupWizard } from './SetupWizard';
|
|
9
10
|
import { useSession } from './useSession';
|
|
10
11
|
|
|
11
12
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
@@ -37,7 +38,10 @@ function copyToClipboard(text: string, renderer: unknown) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function App(props: {
|
|
41
|
+
function App(props: {
|
|
42
|
+
agent: unknown;
|
|
43
|
+
packageJson: Record<string, unknown>;
|
|
44
|
+
}) {
|
|
41
45
|
const renderer = useRenderer();
|
|
42
46
|
const dimensions = useTerminalDimensions();
|
|
43
47
|
const [spinnerIndex, setSpinnerIndex] = createSignal(0);
|
|
@@ -100,11 +104,12 @@ function App(props: { agent: unknown; packageJson: Record<string, unknown> }) {
|
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
useKeyboard((key) => {
|
|
107
|
+
const keyName = String(key.name ?? '').toLowerCase();
|
|
103
108
|
if (state.activeEditor()) {
|
|
104
|
-
if (
|
|
109
|
+
if (keyName === 'escape') state.closeEditor();
|
|
105
110
|
return;
|
|
106
111
|
}
|
|
107
|
-
if (key.ctrl
|
|
112
|
+
if ((key.ctrl || key.meta) && keyName === 'c') {
|
|
108
113
|
if (state.busy()) {
|
|
109
114
|
state.abort();
|
|
110
115
|
return;
|
|
@@ -121,19 +126,19 @@ function App(props: { agent: unknown; packageJson: Record<string, unknown> }) {
|
|
|
121
126
|
}, 1600);
|
|
122
127
|
return;
|
|
123
128
|
}
|
|
124
|
-
if (key.ctrl &&
|
|
129
|
+
if (key.ctrl && keyName === 'q') {
|
|
125
130
|
state.toggleRightTab();
|
|
126
131
|
return;
|
|
127
132
|
}
|
|
128
133
|
if (state.busy()) return;
|
|
129
|
-
if (
|
|
130
|
-
if (
|
|
131
|
-
else if (
|
|
132
|
-
if (
|
|
133
|
-
else if (
|
|
134
|
-
else if (
|
|
135
|
-
else if (
|
|
136
|
-
else if (
|
|
134
|
+
if (keyName === 'tab') state.completeSelected();
|
|
135
|
+
if (keyName === 'pageup') state.scrollConversation(conversationRows());
|
|
136
|
+
else if (keyName === 'pagedown') state.scrollConversation(-conversationRows());
|
|
137
|
+
if (keyName === 'up' && state.slash()) state.moveCompletion(-1);
|
|
138
|
+
else if (keyName === 'down' && state.slash()) state.moveCompletion(1);
|
|
139
|
+
else if (keyName === 'up' && !state.input().includes('\n')) state.historyUp();
|
|
140
|
+
else if (keyName === 'down' && !state.input().includes('\n')) state.historyDown();
|
|
141
|
+
else if (keyName === 'escape') {
|
|
137
142
|
if (state.slash()) state.dismissSlash();
|
|
138
143
|
else state.setInput('');
|
|
139
144
|
}
|
|
@@ -196,10 +201,77 @@ function App(props: { agent: unknown; packageJson: Record<string, unknown> }) {
|
|
|
196
201
|
);
|
|
197
202
|
}
|
|
198
203
|
|
|
199
|
-
export async function runOpenTuiShell({
|
|
204
|
+
export async function runOpenTuiShell({
|
|
205
|
+
agent,
|
|
206
|
+
packageJson,
|
|
207
|
+
}: {
|
|
208
|
+
agent: unknown;
|
|
209
|
+
packageJson: Record<string, unknown>;
|
|
210
|
+
}) {
|
|
200
211
|
await render(() => <App agent={agent} packageJson={packageJson} />, {
|
|
201
212
|
exitOnCtrlC: false,
|
|
202
213
|
useMouse: true,
|
|
203
214
|
targetFps: 30,
|
|
204
215
|
});
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function WizardApp(props: {
|
|
220
|
+
mode: 'startup' | 'setup';
|
|
221
|
+
gaps?: any[];
|
|
222
|
+
initialWorkspaceName?: string;
|
|
223
|
+
initialWorkspacePath?: string | null;
|
|
224
|
+
onDone: () => void;
|
|
225
|
+
}) {
|
|
226
|
+
const renderer = useRenderer();
|
|
227
|
+
const dimensions = useTerminalDimensions();
|
|
228
|
+
const close = () => {
|
|
229
|
+
props.onDone();
|
|
230
|
+
renderer.destroy();
|
|
231
|
+
};
|
|
232
|
+
return (
|
|
233
|
+
<box width="100%" height="100%" backgroundColor="#0B0D12">
|
|
234
|
+
<SetupWizard
|
|
235
|
+
mode={props.mode}
|
|
236
|
+
session={{}}
|
|
237
|
+
gaps={props.gaps}
|
|
238
|
+
width={dimensions().width}
|
|
239
|
+
height={dimensions().height}
|
|
240
|
+
initialRoute={props.mode === 'setup' ? 'workspace-name' : undefined}
|
|
241
|
+
initialWorkspaceName={props.initialWorkspaceName}
|
|
242
|
+
initialWorkspacePath={props.initialWorkspacePath ?? null}
|
|
243
|
+
closeOnDone={props.mode === 'setup'}
|
|
244
|
+
onComplete={close}
|
|
245
|
+
onClose={close}
|
|
246
|
+
/>
|
|
247
|
+
</box>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function runStartupWizard(gaps: any[]) {
|
|
252
|
+
if (!gaps.length) return;
|
|
253
|
+
await new Promise<void>((resolve, reject) => {
|
|
254
|
+
render(() => <WizardApp mode="startup" gaps={gaps} onDone={resolve} />, {
|
|
255
|
+
exitOnCtrlC: false,
|
|
256
|
+
useMouse: true,
|
|
257
|
+
targetFps: 30,
|
|
258
|
+
}).catch(reject);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export async function runSetupWizard(options: { workspaceName?: string; workspacePath?: string | null } = {}) {
|
|
263
|
+
await new Promise<void>((resolve, reject) => {
|
|
264
|
+
render(() => (
|
|
265
|
+
<WizardApp
|
|
266
|
+
mode="setup"
|
|
267
|
+
initialWorkspaceName={options.workspaceName}
|
|
268
|
+
initialWorkspacePath={options.workspacePath ?? null}
|
|
269
|
+
onDone={resolve}
|
|
270
|
+
/>
|
|
271
|
+
), {
|
|
272
|
+
exitOnCtrlC: false,
|
|
273
|
+
useMouse: true,
|
|
274
|
+
targetFps: 30,
|
|
275
|
+
}).catch(reject);
|
|
276
|
+
});
|
|
205
277
|
}
|
package/src/shell/useSession.ts
CHANGED
|
@@ -86,14 +86,22 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
|
|
|
86
86
|
session.llm ? 'llm ready' : 'llm limited',
|
|
87
87
|
].join(' ');
|
|
88
88
|
});
|
|
89
|
-
const
|
|
89
|
+
const matchContext = createMemo(() => {
|
|
90
90
|
if (input() === dismissedSlashInput()) return null;
|
|
91
|
-
|
|
91
|
+
return completionContext(input(), session);
|
|
92
|
+
});
|
|
93
|
+
const slash = createMemo(() => {
|
|
94
|
+
const context = matchContext();
|
|
92
95
|
if (!context) return null;
|
|
96
|
+
const selected = Math.min(selectedCompletion(), Math.max(0, context.matches.length - 1));
|
|
97
|
+
const visibleCount = 10;
|
|
98
|
+
const start = Math.max(0, Math.min(selected - Math.floor(visibleCount / 2), Math.max(0, context.matches.length - visibleCount)));
|
|
99
|
+
const visibleMatches = context.matches.slice(start, start + visibleCount);
|
|
93
100
|
return {
|
|
94
101
|
...context,
|
|
95
|
-
selected
|
|
96
|
-
|
|
102
|
+
selected,
|
|
103
|
+
visibleSelected: selected - start,
|
|
104
|
+
items: visibleMatches.map((value: string) => ({
|
|
97
105
|
value,
|
|
98
106
|
description: completionDescription(value, context.parts),
|
|
99
107
|
})),
|
|
@@ -249,8 +257,8 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
|
|
|
249
257
|
|
|
250
258
|
function completeSelected() {
|
|
251
259
|
const context = slash();
|
|
252
|
-
if (!context || context.
|
|
253
|
-
const selected = context.
|
|
260
|
+
if (!context || context.matches.length === 0) return;
|
|
261
|
+
const selected = context.matches[context.selected];
|
|
254
262
|
if (!selected) return;
|
|
255
263
|
const lastSpace = input().lastIndexOf(' ');
|
|
256
264
|
const base = input().endsWith(' ') ? input() : input().slice(0, lastSpace + 1);
|
|
@@ -259,7 +267,7 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
|
|
|
259
267
|
}
|
|
260
268
|
|
|
261
269
|
function moveCompletion(delta: number) {
|
|
262
|
-
const count = slash()?.
|
|
270
|
+
const count = slash()?.matches.length ?? 0;
|
|
263
271
|
if (!count) return;
|
|
264
272
|
setSelectedCompletion((value) => (value + delta + count) % count);
|
|
265
273
|
}
|
package/wiki-workspace
CHANGED
|
@@ -211,7 +211,7 @@ agents_compose() {
|
|
|
211
211
|
read_lines_into_array cacert_args cacert_compose_args "$agents_compose_file" "agents.cacert.compose.yml"
|
|
212
212
|
WORKSPACES_ROOT="$workspaces_root" AGENTS_DATA_DIR="$agents_data_dir" \
|
|
213
213
|
docker compose --project-directory "$DEFAULT_MANAGER_DIR" \
|
|
214
|
-
"${compose_env_args[@]}" -f "$agents_compose_file" "${cacert_args[@]}" -p wiki-agents "$@"
|
|
214
|
+
${compose_env_args[@]+"${compose_env_args[@]}"} -f "$agents_compose_file" ${cacert_args[@]+"${cacert_args[@]}"} -p wiki-agents "$@"
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
case "$subcommand" in
|
|
@@ -496,7 +496,7 @@ compose_for_workspace() {
|
|
|
496
496
|
WIKI_MCP_PORT="$mcp_port" \
|
|
497
497
|
PRODUCTION_MCP_PORT="$prod_port" \
|
|
498
498
|
docker compose --project-directory "$DEFAULT_MANAGER_DIR" \
|
|
499
|
-
"${compose_env_args[@]}" -f "$ROOT_DIR/docker-compose.yml" "${cacert_args[@]}" -p "$project" "$@"
|
|
499
|
+
${compose_env_args[@]+"${compose_env_args[@]}"} -f "$ROOT_DIR/docker-compose.yml" ${cacert_args[@]+"${cacert_args[@]}"} -p "$project" "$@"
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
logs_args() {
|
|
@@ -721,7 +721,7 @@ config_workspace() {
|
|
|
721
721
|
WIKI_SERVE_PORT="$serve_port" \
|
|
722
722
|
WIKI_MCP_PORT="$mcp_port" \
|
|
723
723
|
docker compose --project-directory "$DEFAULT_MANAGER_DIR" \
|
|
724
|
-
"${compose_env_args[@]}" -f "$ROOT_DIR/docker-compose.yml" "${cacert_args[@]}" run --rm wiki init
|
|
724
|
+
${compose_env_args[@]+"${compose_env_args[@]}"} -f "$ROOT_DIR/docker-compose.yml" ${cacert_args[@]+"${cacert_args[@]}"} run --rm wiki init
|
|
725
725
|
|
|
726
726
|
printf 'Workspace ready: %s\n' "$target_path"
|
|
727
727
|
printf 'Start with: wiki-workspace up %s\n' "$workspace"
|