@dotdrelle/wiki-manager 0.6.34 → 0.6.47
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/package.json +2 -2
- package/src/agent/graph.js +2 -19
- package/src/commands/slash.js +17 -1
- package/src/commands/slash.test.js +78 -0
- package/src/core/compose.js +2 -2
- package/src/core/mcp.js +1 -1
- package/src/core/startupCheck.js +1 -1
- package/src/core/startupCheck.test.js +66 -0
- package/src/core/wikirc.js +2 -2
- package/src/shell/repl.js +1 -0
- package/wiki-workspace +16 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdrelle/wiki-manager",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.47",
|
|
4
4
|
"description": "Agentic shell and orchestration cockpit for llm-wiki workspaces.",
|
|
5
5
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
6
6
|
"author": "dotrelle",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"start": "bun ./bin/wiki-manager.js",
|
|
14
|
-
"test": "node --test src/core/activity.test.js src/core/agentEvents.test.js src/core/plan.test.js src/core/mcp.test.js src/core/documentIntake.test.js src/core/wikirc.test.js src/core/modelFetch.test.js src/commands/slash.test.js",
|
|
14
|
+
"test": "node --test src/core/activity.test.js src/core/agentEvents.test.js src/core/plan.test.js src/core/mcp.test.js src/core/documentIntake.test.js src/core/wikirc.test.js src/core/modelFetch.test.js src/core/startupCheck.test.js src/commands/slash.test.js",
|
|
15
15
|
"check": "bun ./bin/wiki-manager.js --version && bun ./bin/wiki-manager.js --help && bun ./bin/wiki-manager.js --once \"verifie le mode agent\""
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|
package/src/agent/graph.js
CHANGED
|
@@ -43,7 +43,7 @@ const SHELL_RUN_COMMAND_TOOL = {
|
|
|
43
43
|
properties: {
|
|
44
44
|
command: {
|
|
45
45
|
type: 'string',
|
|
46
|
-
description: 'Slash command to run, for example "/workspace list", "/workspace init
|
|
46
|
+
description: 'Slash command to run, for example "/workspace list", "/workspace init <name>", or "/use <workspace>".',
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
required: ['command'],
|
|
@@ -366,23 +366,6 @@ export function buildAgentSystemPrompt(state) {
|
|
|
366
366
|
export function buildLimitedAgentResponse(state, reason = 'no workspace loaded with .wikirc.yaml') {
|
|
367
367
|
const workspace = state.session.workspace ?? 'no workspace selected';
|
|
368
368
|
const wikirc = state.session.wikirc?.profile ?? 'no profile loaded';
|
|
369
|
-
const language = state.session.language ?? 'en-US';
|
|
370
|
-
if (language.toLowerCase().startsWith('fr')) {
|
|
371
|
-
return [
|
|
372
|
-
`Donna est active. Workspace courant: ${workspace}.`,
|
|
373
|
-
`Profil wikirc courant: ${wikirc}.`,
|
|
374
|
-
'',
|
|
375
|
-
"Je suis le mode agent du shell: utilise `/agent` pour router les entrees libres vers ce graphe LangGraph, ou `/chat` pour revenir au chat direct.",
|
|
376
|
-
`Connexion LLM: mode limite (${reason}).`,
|
|
377
|
-
`Primitives disponibles maintenant: ${commandList(state.session)}.`,
|
|
378
|
-
'',
|
|
379
|
-
'Outils MCP connectes:',
|
|
380
|
-
formatMcpToolsForAgent(state.session.mcp),
|
|
381
|
-
'',
|
|
382
|
-
'Mode limite: workspace, Docker Compose, appels MCP, echappatoire /wiki, decouverte skills et mode headless sont branches.',
|
|
383
|
-
"Utilise `/help` pour voir les commandes deterministes disponibles.",
|
|
384
|
-
].join('\n');
|
|
385
|
-
}
|
|
386
369
|
return [
|
|
387
370
|
`Donna is active. Current workspace: ${workspace}.`,
|
|
388
371
|
`Current wikirc profile: ${wikirc}.`,
|
|
@@ -506,7 +489,7 @@ export function createAgentGraph(options = {}) {
|
|
|
506
489
|
} catch (err) {
|
|
507
490
|
if (err.name === 'AbortError') throw err;
|
|
508
491
|
const message = err instanceof Error ? err.message : String(err);
|
|
509
|
-
return { response: buildLimitedAgentResponse(state, `LLM
|
|
492
|
+
return { response: buildLimitedAgentResponse(state, `LLM unavailable: ${message}`), pendingToolCalls: null, readyToStream: false };
|
|
510
493
|
}
|
|
511
494
|
}
|
|
512
495
|
|
package/src/commands/slash.js
CHANGED
|
@@ -282,6 +282,13 @@ function workspaceStatsText(stats) {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
function workspaceLoadedText(workspace, summary, session) {
|
|
285
|
+
const profiles = listWikircProfiles(workspace.workspacePath);
|
|
286
|
+
const profileLines = profiles.length > 0
|
|
287
|
+
? profiles.map((profile) => {
|
|
288
|
+
const marker = profile.name === summary.profile ? '*' : ' ';
|
|
289
|
+
return `${marker} ${profile.name}\t${profile.fileName}`;
|
|
290
|
+
})
|
|
291
|
+
: ['No .wikirc.yaml profile found.'];
|
|
285
292
|
return [
|
|
286
293
|
`Workspace: ${workspace.name}`,
|
|
287
294
|
'',
|
|
@@ -300,6 +307,12 @@ function workspaceLoadedText(workspace, summary, session) {
|
|
|
300
307
|
`vector: ${summary.vectorEnabled ? 'enabled' : 'disabled'}`,
|
|
301
308
|
`embedding: ${summary.embeddingModel ?? '-'}`,
|
|
302
309
|
'',
|
|
310
|
+
'Available configs',
|
|
311
|
+
'',
|
|
312
|
+
...profileLines,
|
|
313
|
+
'',
|
|
314
|
+
`Switch config: /config use <profile>`,
|
|
315
|
+
'',
|
|
303
316
|
'Session',
|
|
304
317
|
'',
|
|
305
318
|
`llm: ${session.llm ? 'configured' : 'missing config'}`,
|
|
@@ -647,7 +660,7 @@ Modes:
|
|
|
647
660
|
Status:
|
|
648
661
|
Agent-first shell is installed with workspace services, MCP calls, wiki CLI, skill discovery, and headless runs.
|
|
649
662
|
Shell UI is English. Agent exchange language is read from the active .wikirc.yaml.
|
|
650
|
-
LLM config is intentionally workspace-scoped and
|
|
663
|
+
LLM config is intentionally workspace-scoped and is read from .wikirc.yaml after /use <workspace>.
|
|
651
664
|
Headless mode supports one-shot workspace prompts and skill runs with log output.
|
|
652
665
|
`;
|
|
653
666
|
}
|
|
@@ -690,6 +703,9 @@ export async function handleSlashCommand(line, context) {
|
|
|
690
703
|
case 'use': {
|
|
691
704
|
const workspaceName = args[1];
|
|
692
705
|
if (!workspaceName) {
|
|
706
|
+
return { output: formatWorkspaceList(listWorkspaces(), context.session) };
|
|
707
|
+
}
|
|
708
|
+
if (args[2]) {
|
|
693
709
|
return { output: 'Usage: /use <workspace>' };
|
|
694
710
|
}
|
|
695
711
|
const workspace = findWorkspace(workspaceName);
|
|
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import test from 'node:test';
|
|
7
7
|
import { handleSlashCommand } from './slash.js';
|
|
8
|
+
import { completionContext } from '../shell/repl.js';
|
|
8
9
|
|
|
9
10
|
test('/workspace delete removes files and clears current session context after confirmation', async () => {
|
|
10
11
|
const root = await mkdtemp(join(tmpdir(), 'wiki-manager-delete-workspace-'));
|
|
@@ -66,3 +67,80 @@ test('/new without a name shows usage', async () => {
|
|
|
66
67
|
|
|
67
68
|
assert.match(result.output ?? '', /Usage/i);
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
test('/use loads only workspaces and /config use switches wikirc profiles', async () => {
|
|
72
|
+
const root = await mkdtemp(join(tmpdir(), 'wiki-manager-use-profile-'));
|
|
73
|
+
const registryRoot = join(root, 'registry');
|
|
74
|
+
const registryPath = join(registryRoot, 'demo');
|
|
75
|
+
const workspacePath = join(root, 'workspace');
|
|
76
|
+
mkdirSync(registryPath, { recursive: true });
|
|
77
|
+
mkdirSync(workspacePath, { recursive: true });
|
|
78
|
+
writeFileSync(join(registryPath, '.env'), [
|
|
79
|
+
'WORKSPACE_NAME=demo',
|
|
80
|
+
`WIKI_WORKSPACE_PATH=${workspacePath}`,
|
|
81
|
+
'',
|
|
82
|
+
].join('\n'), 'utf8');
|
|
83
|
+
writeFileSync(join(workspacePath, '.wikirc.yaml'), [
|
|
84
|
+
'language: fr',
|
|
85
|
+
'llm:',
|
|
86
|
+
' provider: default-provider',
|
|
87
|
+
' model: default-model',
|
|
88
|
+
'',
|
|
89
|
+
].join('\n'), 'utf8');
|
|
90
|
+
writeFileSync(join(workspacePath, '.wikirc.yaml.vpn'), [
|
|
91
|
+
'language: fr',
|
|
92
|
+
'llm:',
|
|
93
|
+
' provider: vpn-provider',
|
|
94
|
+
' model: vpn-model',
|
|
95
|
+
'',
|
|
96
|
+
].join('\n'), 'utf8');
|
|
97
|
+
|
|
98
|
+
const previousDir = process.env.WIKI_WORKSPACES_DIR;
|
|
99
|
+
process.env.WIKI_WORKSPACES_DIR = registryRoot;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const session = {};
|
|
103
|
+
const listResult = await handleSlashCommand('/use', {
|
|
104
|
+
packageJson: { version: 'test' },
|
|
105
|
+
session,
|
|
106
|
+
});
|
|
107
|
+
assert.match(listResult.output ?? '', /Workspaces/);
|
|
108
|
+
assert.match(listResult.output ?? '', /demo\tavailable/);
|
|
109
|
+
assert.doesNotMatch(listResult.output ?? '', /vpn\t\.wikirc\.yaml\.vpn/);
|
|
110
|
+
|
|
111
|
+
const useResult = await handleSlashCommand('/use demo', {
|
|
112
|
+
packageJson: { version: 'test' },
|
|
113
|
+
session,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
assert.equal(session.workspace, 'demo');
|
|
117
|
+
assert.equal(session.wikirc.profile, 'default');
|
|
118
|
+
assert.match(useResult.output ?? '', /profile: default/);
|
|
119
|
+
assert.match(useResult.output ?? '', /\* default\t\.wikirc\.yaml/);
|
|
120
|
+
assert.match(useResult.output ?? '', /vpn\t\.wikirc\.yaml\.vpn/);
|
|
121
|
+
assert.match(useResult.output ?? '', /Switch config: \/config use <profile>/);
|
|
122
|
+
|
|
123
|
+
const invalidUse = await handleSlashCommand('/use demo vpn', {
|
|
124
|
+
packageJson: { version: 'test' },
|
|
125
|
+
session,
|
|
126
|
+
});
|
|
127
|
+
assert.match(invalidUse.output ?? '', /Usage: \/use <workspace>/);
|
|
128
|
+
assert.equal(session.wikirc.profile, 'default');
|
|
129
|
+
|
|
130
|
+
const result = await handleSlashCommand('/config use vpn', {
|
|
131
|
+
packageJson: { version: 'test' },
|
|
132
|
+
session,
|
|
133
|
+
});
|
|
134
|
+
assert.equal(session.workspace, 'demo');
|
|
135
|
+
assert.equal(session.wikirc.profile, 'vpn');
|
|
136
|
+
assert.match(result.output ?? '', /profile=vpn/);
|
|
137
|
+
|
|
138
|
+
const completion = completionContext('/use ', { commands: ['use'] });
|
|
139
|
+
assert.deepEqual(completion?.matches, ['demo']);
|
|
140
|
+
const configCompletion = completionContext('/config use ', session);
|
|
141
|
+
assert.deepEqual(configCompletion?.matches, ['default', 'vpn']);
|
|
142
|
+
} finally {
|
|
143
|
+
if (previousDir === undefined) delete process.env.WIKI_WORKSPACES_DIR;
|
|
144
|
+
else process.env.WIKI_WORKSPACES_DIR = previousDir;
|
|
145
|
+
}
|
|
146
|
+
});
|
package/src/core/compose.js
CHANGED
|
@@ -113,6 +113,7 @@ function composeEnv(session) {
|
|
|
113
113
|
...cacertEnv(),
|
|
114
114
|
WORKSPACE_NAME: session.workspace,
|
|
115
115
|
WIKI_WORKSPACE_PATH: session.workspacePath,
|
|
116
|
+
...(session.wikirc?.fileName && { WIKI_CONFIG_PATH: session.wikirc.fileName }),
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -303,8 +304,7 @@ export async function runWikiCli(session, args, options = {}) {
|
|
|
303
304
|
if (!Array.isArray(args) || args.length === 0) {
|
|
304
305
|
throw new Error('Usage: /wiki run <args...>');
|
|
305
306
|
}
|
|
306
|
-
|
|
307
|
-
return runCompose(session, ['run', '--rm', ...configEnv, 'wiki', ...args], {
|
|
307
|
+
return runCompose(session, ['run', '--rm', 'wiki', ...args], {
|
|
308
308
|
timeout: options.timeout ?? 180_000,
|
|
309
309
|
maxBuffer: options.maxBuffer ?? 1024 * 1024 * 8,
|
|
310
310
|
onOutput: options.onOutput,
|
package/src/core/mcp.js
CHANGED
|
@@ -211,7 +211,7 @@ async function mcpRequest(endpoint, method, params, signal, options = {}) {
|
|
|
211
211
|
params: {
|
|
212
212
|
protocolVersion: '2025-06-18',
|
|
213
213
|
capabilities: {},
|
|
214
|
-
clientInfo: { name: 'wiki-manager', version: '0.6.
|
|
214
|
+
clientInfo: { name: 'wiki-manager', version: '0.6.47' },
|
|
215
215
|
},
|
|
216
216
|
}),
|
|
217
217
|
});
|
package/src/core/startupCheck.js
CHANGED
|
@@ -92,7 +92,7 @@ function checkWikirc(workspace) {
|
|
|
92
92
|
profileName: loaded.profile.name,
|
|
93
93
|
};
|
|
94
94
|
const gaps = [];
|
|
95
|
-
if (!summary.hasApiKey || !summary.model) gaps.push({ kind: 'llm', context });
|
|
95
|
+
if (!summary.provider || !summary.baseUrl || !summary.hasApiKey || !summary.model) gaps.push({ kind: 'llm', context });
|
|
96
96
|
if (!summary.vectorEnabled) gaps.push({ kind: 'vector', context });
|
|
97
97
|
return gaps;
|
|
98
98
|
} catch {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { runChecks } from './startupCheck.js';
|
|
8
|
+
|
|
9
|
+
async function withWorkspace(wikircLines, fn) {
|
|
10
|
+
const root = await mkdtemp(join(tmpdir(), 'wiki-manager-startup-check-'));
|
|
11
|
+
const registryRoot = join(root, 'registry');
|
|
12
|
+
const registryPath = join(registryRoot, 'demo');
|
|
13
|
+
const workspacePath = join(root, 'workspace');
|
|
14
|
+
mkdirSync(registryPath, { recursive: true });
|
|
15
|
+
mkdirSync(workspacePath, { recursive: true });
|
|
16
|
+
writeFileSync(join(registryPath, '.env'), [
|
|
17
|
+
'WORKSPACE_NAME=demo',
|
|
18
|
+
`WIKI_WORKSPACE_PATH=${workspacePath}`,
|
|
19
|
+
'',
|
|
20
|
+
].join('\n'), 'utf8');
|
|
21
|
+
writeFileSync(join(workspacePath, '.wikirc.yaml'), [...wikircLines, ''].join('\n'), 'utf8');
|
|
22
|
+
|
|
23
|
+
const previousDir = process.env.WIKI_WORKSPACES_DIR;
|
|
24
|
+
process.env.WIKI_WORKSPACES_DIR = registryRoot;
|
|
25
|
+
try {
|
|
26
|
+
await fn({ workspacePath });
|
|
27
|
+
} finally {
|
|
28
|
+
if (previousDir === undefined) delete process.env.WIKI_WORKSPACES_DIR;
|
|
29
|
+
else process.env.WIKI_WORKSPACES_DIR = previousDir;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function hasGap(gaps, kind) {
|
|
34
|
+
return gaps.some((gap) => gap.kind === kind);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test('runChecks treats default LLM config without baseUrl as incomplete', async () => {
|
|
38
|
+
await withWorkspace([
|
|
39
|
+
'llm:',
|
|
40
|
+
' provider: openai-compatible',
|
|
41
|
+
' apiKey: key',
|
|
42
|
+
' model: chat-model',
|
|
43
|
+
'retrieval:',
|
|
44
|
+
' vector:',
|
|
45
|
+
' enabled: true',
|
|
46
|
+
], async () => {
|
|
47
|
+
const gaps = await runChecks();
|
|
48
|
+
assert.equal(hasGap(gaps, 'llm'), true);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('runChecks treats default LLM config with provider, baseUrl, apiKey and model as complete', async () => {
|
|
53
|
+
await withWorkspace([
|
|
54
|
+
'llm:',
|
|
55
|
+
' provider: openai-compatible',
|
|
56
|
+
' baseUrl: http://localhost:8000/v1',
|
|
57
|
+
' apiKey: key',
|
|
58
|
+
' model: chat-model',
|
|
59
|
+
'retrieval:',
|
|
60
|
+
' vector:',
|
|
61
|
+
' enabled: true',
|
|
62
|
+
], async () => {
|
|
63
|
+
const gaps = await runChecks();
|
|
64
|
+
assert.equal(hasGap(gaps, 'llm'), false);
|
|
65
|
+
});
|
|
66
|
+
});
|
package/src/core/wikirc.js
CHANGED
|
@@ -29,8 +29,8 @@ export function resolveWikircProfile(workspacePath, profileName = 'default') {
|
|
|
29
29
|
const normalized = profileName || 'default';
|
|
30
30
|
const found = profiles.find((profile) => profile.name === normalized || profile.fileName === normalized);
|
|
31
31
|
if (!found) {
|
|
32
|
-
const available = profiles.map((profile) => profile.name).join(', ') || '
|
|
33
|
-
throw new Error(`
|
|
32
|
+
const available = profiles.map((profile) => profile.name).join(', ') || 'none';
|
|
33
|
+
throw new Error(`wikirc profile not found: ${normalized} (available: ${available})`);
|
|
34
34
|
}
|
|
35
35
|
return found;
|
|
36
36
|
}
|
package/src/shell/repl.js
CHANGED
|
@@ -202,6 +202,7 @@ function completionValuesFor(parts, inputBuffer, session) {
|
|
|
202
202
|
if (tokenIndex === 0) return slashCompletions(session);
|
|
203
203
|
if (command === '/new' && tokenIndex === 1) return [];
|
|
204
204
|
if (command === '/use' && tokenIndex === 1) return workspaceNames();
|
|
205
|
+
if (command === '/use' && tokenIndex === 2) return [];
|
|
205
206
|
if (command === '/config' && tokenIndex === 1) return ['edit', 'list', 'status', 'use'];
|
|
206
207
|
if (command === '/config' && (previousToken === 'use' || previousToken === 'edit')) return wikircProfileNames(session);
|
|
207
208
|
if (command === '/mcp' && tokenIndex === 1) return ['call', 'endpoints', 'status', 'tools'];
|
package/wiki-workspace
CHANGED
|
@@ -225,9 +225,14 @@ agents_compose() {
|
|
|
225
225
|
mkdir -p "$agents_data_dir/cme" "$agents_data_dir/documents/input" "$agents_data_dir/documents/output" "$agents_data_dir/documents/uploads"
|
|
226
226
|
mkdir -p "$workspaces_root"
|
|
227
227
|
$do_pull && _agents_dc pull
|
|
228
|
-
|
|
228
|
+
local up_args=(up -d)
|
|
229
|
+
if [[ -n "$CACERT_PATH" ]]; then
|
|
230
|
+
up_args+=(--force-recreate)
|
|
231
|
+
fi
|
|
232
|
+
_agents_dc "${up_args[@]}"
|
|
229
233
|
printf 'Workspaces root: %s\n' "$workspaces_root"
|
|
230
234
|
printf 'Agents data: %s\n' "$agents_data_dir"
|
|
235
|
+
[[ -z "$CACERT_PATH" ]] || printf 'CA cert: %s -> /wiki-manager-ca.pem\n' "$CACERT_PATH"
|
|
231
236
|
printf 'Agents: cme=:%s documents=:%s mailer=:%s\n' \
|
|
232
237
|
"${CME_MCP_PORT:-3336}" "${DOCUMENTS_MCP_PORT:-3337}" "${MAILER_MCP_PORT:-3335}"
|
|
233
238
|
;;
|
|
@@ -524,20 +529,19 @@ cacert_compose_args() {
|
|
|
524
529
|
local compose_file="$1"
|
|
525
530
|
local override_name="$2"
|
|
526
531
|
[[ -n "$CACERT_PATH" ]] || return 0
|
|
527
|
-
command -v node >/dev/null 2>&1 || die "--cacert requires node to generate Docker Compose overrides"
|
|
528
532
|
|
|
529
533
|
local services
|
|
530
534
|
services="$(
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
|
|
535
|
+
awk '
|
|
536
|
+
/^[[:space:]]*services:[[:space:]]*$/ { in_services=1; next }
|
|
537
|
+
in_services && /^[^[:space:]#]/ { exit }
|
|
538
|
+
in_services && /^ [A-Za-z0-9_.-]+:[[:space:]]*$/ {
|
|
539
|
+
line=$0
|
|
540
|
+
sub(/^ /, "", line)
|
|
541
|
+
sub(/:[[:space:]]*$/, "", line)
|
|
542
|
+
print line
|
|
543
|
+
}
|
|
544
|
+
' "$compose_file"
|
|
541
545
|
)"
|
|
542
546
|
[[ -n "$services" ]] || return 0
|
|
543
547
|
|