@alecsibilia/luca 13.0.0-alpha.5 → 13.0.0-alpha.6
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/dist/chunks/muninn-mcp.mjs +3 -29
- package/dist/chunks/vault-init.mjs +36 -25
- package/dist/index.mjs +1 -1
- package/dist/shared/luca.ChU85KSu.mjs +32 -0
- package/package.json +1 -1
|
@@ -1,36 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { i as isMuninnRegistered } from '../shared/luca.ChU85KSu.mjs';
|
|
2
|
+
import 'node:os';
|
|
3
|
+
import 'node:path';
|
|
3
4
|
|
|
4
5
|
const CHECK_NAME = "MuninnDB MCP wiring";
|
|
5
6
|
const MCP_URL = "http://127.0.0.1:8750/mcp";
|
|
6
7
|
const ADD_COMMAND = 'claude mcp add --transport sse muninn http://localhost:8750/mcp --header "Authorization: Bearer <your-muninn-api-key>"';
|
|
7
|
-
async function readJsonObject(path) {
|
|
8
|
-
try {
|
|
9
|
-
const file = Bun.file(path);
|
|
10
|
-
if (!await file.exists()) return null;
|
|
11
|
-
const parsed = JSON.parse(await file.text());
|
|
12
|
-
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
13
|
-
} catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function hasMuninnEntry(mcpServers) {
|
|
18
|
-
return mcpServers !== null && typeof mcpServers === "object" && "muninn" in mcpServers;
|
|
19
|
-
}
|
|
20
|
-
async function isMuninnRegistered(cwd) {
|
|
21
|
-
const projectMcp = await readJsonObject(join(cwd, ".mcp.json"));
|
|
22
|
-
if (hasMuninnEntry(projectMcp?.mcpServers)) return true;
|
|
23
|
-
const userConfig = await readJsonObject(join(homedir(), ".claude.json"));
|
|
24
|
-
if (hasMuninnEntry(userConfig?.mcpServers)) return true;
|
|
25
|
-
const projects = userConfig?.projects;
|
|
26
|
-
if (projects !== null && typeof projects === "object") {
|
|
27
|
-
const project = projects[cwd];
|
|
28
|
-
if (project !== null && typeof project === "object" && hasMuninnEntry(project.mcpServers)) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
8
|
async function isMcpReachable() {
|
|
35
9
|
try {
|
|
36
10
|
const res = await fetch(MCP_URL, {
|
|
@@ -12,6 +12,7 @@ import { defineCommand } from 'citty';
|
|
|
12
12
|
import { join, basename } from 'pathe';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { d as checkMuninndbService, c as resolveMuninndbPort } from '../shared/luca.BfjhRHhj.mjs';
|
|
15
|
+
import { i as isMuninnRegistered } from '../shared/luca.ChU85KSu.mjs';
|
|
15
16
|
import 'node:os';
|
|
16
17
|
import '../shared/luca.DTomPq7I.mjs';
|
|
17
18
|
import 'semver';
|
|
@@ -84,8 +85,14 @@ function sanitizeJsonParse(json) {
|
|
|
84
85
|
const VaultConfigSchema = z.object({
|
|
85
86
|
/** Sanitized vault name in lowercase kebab-case. */
|
|
86
87
|
vaultName: z.string().min(1),
|
|
87
|
-
/**
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* MuninnDB API key. OPTIONAL — it is an instance-level credential needed
|
|
90
|
+
* only ONCE to register the MuninnDB MCP server, which then reaches every
|
|
91
|
+
* vault (the vault is a per-tool-call parameter). When a `muninn` MCP
|
|
92
|
+
* server is already registered, the wizard records just the vault name and
|
|
93
|
+
* leaves this unset.
|
|
94
|
+
*/
|
|
95
|
+
apiKey: z.string().min(1).optional()
|
|
89
96
|
});
|
|
90
97
|
function muninndbWebUiUrl() {
|
|
91
98
|
return `http://127.0.0.1:${resolveMuninndbPort()}`;
|
|
@@ -123,16 +130,26 @@ async function runVaultWizard(context, cwd = process.cwd()) {
|
|
|
123
130
|
});
|
|
124
131
|
if (p.isCancel(vaultName)) return null;
|
|
125
132
|
const finalVaultName = sanitizeVaultName(String(vaultName));
|
|
133
|
+
if (await isMuninnRegistered(cwd)) {
|
|
134
|
+
p.log.success(
|
|
135
|
+
"MuninnDB MCP server already registered \u2014 it reaches every vault, so no API key is needed here."
|
|
136
|
+
);
|
|
137
|
+
return { vaultName: finalVaultName };
|
|
138
|
+
}
|
|
126
139
|
p.note(
|
|
127
140
|
[
|
|
128
|
-
"
|
|
141
|
+
"No MuninnDB MCP server is registered with Claude Code yet. The key",
|
|
142
|
+
"below is an INSTANCE-level credential, captured once to register",
|
|
143
|
+
"the server \u2014 that single registration then reaches every vault.",
|
|
144
|
+
"",
|
|
145
|
+
"Generate one in the MuninnDB Web UI:",
|
|
129
146
|
"",
|
|
130
147
|
` ${muninndbWebUiUrl()}`,
|
|
131
148
|
"",
|
|
132
|
-
"
|
|
133
|
-
"
|
|
149
|
+
"Settings > API Keys > create a new key. You can also skip this and",
|
|
150
|
+
"register the server later (see `luca doctor`)."
|
|
134
151
|
].join("\n"),
|
|
135
|
-
"API Key"
|
|
152
|
+
"API Key (one-time)"
|
|
136
153
|
);
|
|
137
154
|
const apiKey = await p.password({
|
|
138
155
|
message: "MuninnDB API key (leave empty to skip)"
|
|
@@ -141,12 +158,12 @@ async function runVaultWizard(context, cwd = process.cwd()) {
|
|
|
141
158
|
const trimmedKey = String(apiKey ?? "").trim();
|
|
142
159
|
if (trimmedKey.length === 0) {
|
|
143
160
|
p.log.warn(
|
|
144
|
-
"No API key provided.
|
|
161
|
+
"No API key provided. Vault name recorded; register the MuninnDB MCP server later (see `luca doctor`)."
|
|
145
162
|
);
|
|
146
|
-
return
|
|
163
|
+
return { vaultName: finalVaultName };
|
|
147
164
|
}
|
|
148
165
|
const confirmed = await p.confirm({
|
|
149
|
-
message: `Set vault "${finalVaultName}"
|
|
166
|
+
message: `Set vault "${finalVaultName}" and write the API key to .env?`,
|
|
150
167
|
initialValue: true
|
|
151
168
|
});
|
|
152
169
|
if (p.isCancel(confirmed) || !confirmed) return null;
|
|
@@ -173,14 +190,8 @@ async function writeVaultConfig(vaultName, configPath) {
|
|
|
173
190
|
config.muninn = { ...existing, vault: vaultName };
|
|
174
191
|
await Bun.write(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
175
192
|
}
|
|
176
|
-
async function writeApiKeyToEnv(apiKey, envPath
|
|
177
|
-
const
|
|
178
|
-
const envLines = [
|
|
179
|
-
`${vaultKey}=${apiKey}`,
|
|
180
|
-
// Default vault key is always needed for cross-cutting memories
|
|
181
|
-
...vaultName && vaultName !== "default" ? [`MUNINN_DB_DEFAULT_API_KEY=${apiKey}`] : [],
|
|
182
|
-
`MUNINN_DB_API_KEY=${apiKey}`
|
|
183
|
-
];
|
|
193
|
+
async function writeApiKeyToEnv(apiKey, envPath) {
|
|
194
|
+
const envLines = [`MUNINN_DB_API_KEY=${apiKey}`];
|
|
184
195
|
const file = Bun.file(envPath);
|
|
185
196
|
if (await file.exists()) {
|
|
186
197
|
let content = await file.text();
|
|
@@ -277,14 +288,14 @@ const vaultInitCommand = defineCommand({
|
|
|
277
288
|
const envPath = join(cwd, ".env");
|
|
278
289
|
await writeVaultConfig(vaultResult.vaultName, configPath);
|
|
279
290
|
p.log.success("Vault name written to .luca/config.json");
|
|
280
|
-
|
|
281
|
-
vaultResult.apiKey,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
if (vaultResult.apiKey) {
|
|
292
|
+
await writeApiKeyToEnv(vaultResult.apiKey, envPath);
|
|
293
|
+
p.log.success("API key written to .env");
|
|
294
|
+
}
|
|
295
|
+
if (existsSync(envPath)) {
|
|
296
|
+
await ensureEnvInGitignore(cwd);
|
|
297
|
+
p.log.success(".env protected in .gitignore");
|
|
298
|
+
}
|
|
288
299
|
const reachable = await verifyVaultConnection(vaultResult.vaultName);
|
|
289
300
|
if (reachable) {
|
|
290
301
|
p.log.success(
|
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
async function readJsonObject(path) {
|
|
5
|
+
try {
|
|
6
|
+
const file = Bun.file(path);
|
|
7
|
+
if (!await file.exists()) return null;
|
|
8
|
+
const parsed = JSON.parse(await file.text());
|
|
9
|
+
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function hasMuninnEntry(mcpServers) {
|
|
15
|
+
return mcpServers !== null && typeof mcpServers === "object" && "muninn" in mcpServers;
|
|
16
|
+
}
|
|
17
|
+
async function isMuninnRegistered(cwd) {
|
|
18
|
+
const projectMcp = await readJsonObject(join(cwd, ".mcp.json"));
|
|
19
|
+
if (hasMuninnEntry(projectMcp?.mcpServers)) return true;
|
|
20
|
+
const userConfig = await readJsonObject(join(homedir(), ".claude.json"));
|
|
21
|
+
if (hasMuninnEntry(userConfig?.mcpServers)) return true;
|
|
22
|
+
const projects = userConfig?.projects;
|
|
23
|
+
if (projects !== null && typeof projects === "object") {
|
|
24
|
+
const project = projects[cwd];
|
|
25
|
+
if (project !== null && typeof project === "object" && hasMuninnEntry(project.mcpServers)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { isMuninnRegistered as i };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alecsibilia/luca",
|
|
3
3
|
"description": "Luca — spec-driven agentic development workflow for Claude Code (umbrella; bundles luca-cli, luca-core, luca-tools)",
|
|
4
|
-
"version": "13.0.0-alpha.
|
|
4
|
+
"version": "13.0.0-alpha.6",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Alec Sibilia <sibilia.alec@gmail.com>",
|
|
7
7
|
"homepage": "https://github.com/asibilia/luca-framework#readme",
|