- Laputa — Release History
+ Tolaria — Release History
-
Laputa Release History
+
Tolaria Release History
Auto-updated on every release
diff --git a/mcp-server/index.js b/mcp-server/index.js
index dd0b8e68..7a028afe 100644
--- a/mcp-server/index.js
+++ b/mcp-server/index.js
@@ -1,15 +1,15 @@
#!/usr/bin/env node
/**
- * Laputa MCP Server — lightweight vault tools for AI agents.
+ * Tolaria MCP Server — lightweight vault tools for AI agents.
*
* The agent has full shell access (bash, read, write, edit).
- * These MCP tools provide Laputa-specific capabilities that
+ * These MCP tools provide Tolaria-specific capabilities that
* native tools cannot replace:
*
* - search_notes: full-text search across vault notes
* - get_vault_context: vault structure overview (types, note count, folders)
* - get_note: parsed frontmatter + content (convenience over raw cat)
- * - open_note: signal Laputa UI to open a note as a tab
+ * - open_note: signal Tolaria UI to open a note as a tab
* - highlight_editor: visually highlight a UI element (editor, tab, etc.)
* - refresh_vault: trigger vault rescan so new/modified files appear
*/
@@ -87,7 +87,7 @@ const TOOLS = [
},
{
name: 'open_note',
- description: 'Open a note in the Laputa UI as a new tab. Use after creating or editing a note so the user can see it.',
+ description: 'Open a note in the Tolaria UI as a new tab. Use after creating or editing a note so the user can see it.',
inputSchema: {
type: 'object',
properties: {
@@ -98,7 +98,7 @@ const TOOLS = [
},
{
name: 'highlight_editor',
- description: 'Visually highlight a UI element in Laputa (editor, tab, properties panel, or note list). The highlight auto-clears after a short delay.',
+ description: 'Visually highlight a UI element in Tolaria (editor, tab, properties panel, or note list). The highlight auto-clears after a short delay.',
inputSchema: {
type: 'object',
properties: {
@@ -110,7 +110,7 @@ const TOOLS = [
},
{
name: 'refresh_vault',
- description: 'Trigger a vault rescan so new or modified files appear immediately in the Laputa note list.',
+ description: 'Trigger a vault rescan so new or modified files appear immediately in the Tolaria note list.',
inputSchema: {
type: 'object',
properties: {
@@ -152,7 +152,7 @@ function handleOpenNote(args) {
// then signal the UI to open it in a tab.
broadcastUiAction('vault_changed', { path: args.path })
broadcastUiAction('open_tab', { path: args.path })
- return { content: [{ type: 'text', text: `Opening ${args.path} in Laputa` }] }
+ return { content: [{ type: 'text', text: `Opening ${args.path} in Tolaria` }] }
}
function handleHighlightEditor(args) {
@@ -168,7 +168,7 @@ function handleRefreshVault(args) {
// --- Server setup ---
const server = new Server(
- { name: 'laputa-mcp-server', version: '0.3.0' },
+ { name: 'tolaria-mcp-server', version: '0.3.0' },
{ capabilities: { tools: {} } },
)
@@ -195,7 +195,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
- console.error(`Laputa MCP server running (vault: ${VAULT_PATH})`)
+ console.error(`Tolaria MCP server running (vault: ${VAULT_PATH})`)
}
main().catch(console.error)
diff --git a/mcp-server/package-lock.json b/mcp-server/package-lock.json
index 7745fee9..ee3f8d48 100644
--- a/mcp-server/package-lock.json
+++ b/mcp-server/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "laputa-mcp-server",
+ "name": "tolaria-mcp-server",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "laputa-mcp-server",
+ "name": "tolaria-mcp-server",
"version": "0.1.0",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
diff --git a/mcp-server/package.json b/mcp-server/package.json
index 5443a161..7afa614e 100644
--- a/mcp-server/package.json
+++ b/mcp-server/package.json
@@ -1,7 +1,7 @@
{
- "name": "laputa-mcp-server",
+ "name": "tolaria-mcp-server",
"version": "0.1.0",
- "description": "MCP server for Laputa vault operations",
+ "description": "MCP server for Tolaria vault operations",
"type": "module",
"main": "index.js",
"scripts": {
diff --git a/mcp-server/ws-bridge.js b/mcp-server/ws-bridge.js
index 013534ab..29dc81fa 100644
--- a/mcp-server/ws-bridge.js
+++ b/mcp-server/ws-bridge.js
@@ -1,8 +1,8 @@
#!/usr/bin/env node
/**
- * WebSocket bridge for Laputa MCP tools.
+ * WebSocket bridge for Tolaria MCP tools.
*
- * Exposes vault operations over WebSocket so the Laputa app frontend
+ * Exposes vault operations over WebSocket so the Tolaria app frontend
* can invoke MCP tools in real-time without going through stdio.
*
* Port 9710: Tool bridge — Claude/AI clients call vault tools here.
@@ -74,7 +74,7 @@ async function handleMessage(data) {
/**
* Attempt to start the UI bridge WebSocket server.
* Returns a Promise that resolves to the WebSocketServer or null if the port
- * is unavailable (e.g. another Laputa instance owns it).
+ * is unavailable (e.g. another Tolaria instance owns it).
*/
export function startUiBridge(port = WS_UI_PORT) {
return new Promise((resolve) => {
diff --git a/package.json b/package.json
index 4f987390..b766a25b 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "laputa-app",
+ "name": "tolaria",
"private": true,
"version": "0.1.0",
"type": "module",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index c86730c9..5cb35576 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -2271,37 +2271,6 @@ dependencies = [
"selectors",
]
-[[package]]
-name = "laputa"
-version = "0.1.0"
-dependencies = [
- "base64 0.22.1",
- "chrono",
- "dirs 5.0.1",
- "futures-util",
- "gray_matter",
- "log",
- "mockito",
- "regex",
- "reqwest 0.12.28",
- "sentry",
- "serde",
- "serde_json",
- "serde_yaml",
- "tauri",
- "tauri-build",
- "tauri-plugin-dialog",
- "tauri-plugin-log",
- "tauri-plugin-opener",
- "tauri-plugin-prevent-default",
- "tauri-plugin-process",
- "tauri-plugin-updater",
- "tempfile",
- "tokio",
- "uuid",
- "walkdir",
-]
-
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -5264,6 +5233,37 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tolaria"
+version = "0.1.0"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "dirs 5.0.1",
+ "futures-util",
+ "gray_matter",
+ "log",
+ "mockito",
+ "regex",
+ "reqwest 0.12.28",
+ "sentry",
+ "serde",
+ "serde_json",
+ "serde_yaml",
+ "tauri",
+ "tauri-build",
+ "tauri-plugin-dialog",
+ "tauri-plugin-log",
+ "tauri-plugin-opener",
+ "tauri-plugin-prevent-default",
+ "tauri-plugin-process",
+ "tauri-plugin-updater",
+ "tempfile",
+ "tokio",
+ "uuid",
+ "walkdir",
+]
+
[[package]]
name = "toml"
version = "0.5.11"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 9079a382..601cad2d 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "laputa"
+name = "tolaria"
version = "0.1.0"
description = "Personal knowledge and life management app"
authors = ["you"]
@@ -9,7 +9,7 @@ edition = "2021"
rust-version = "1.77.2"
[lib]
-name = "laputa_lib"
+name = "tolaria_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
diff --git a/src-tauri/gen/apple/assets/mcp-server/index.js b/src-tauri/gen/apple/assets/mcp-server/index.js
index bebf4d8d..7bbf091a 100755
--- a/src-tauri/gen/apple/assets/mcp-server/index.js
+++ b/src-tauri/gen/apple/assets/mcp-server/index.js
@@ -28077,7 +28077,7 @@ var TOOLS = [
},
{
name: "open_note",
- description: "Open a note in the Laputa UI as a new tab. Use after creating or editing a note so the user can see it.",
+ description: "Open a note in the Tolaria UI as a new tab. Use after creating or editing a note so the user can see it.",
inputSchema: {
type: "object",
properties: {
@@ -28088,7 +28088,7 @@ var TOOLS = [
},
{
name: "highlight_editor",
- description: "Visually highlight a UI element in Laputa (editor, tab, properties panel, or note list). The highlight auto-clears after a short delay.",
+ description: "Visually highlight a UI element in Tolaria (editor, tab, properties panel, or note list). The highlight auto-clears after a short delay.",
inputSchema: {
type: "object",
properties: {
@@ -28100,7 +28100,7 @@ var TOOLS = [
},
{
name: "refresh_vault",
- description: "Trigger a vault rescan so new or modified files appear immediately in the Laputa note list.",
+ description: "Trigger a vault rescan so new or modified files appear immediately in the Tolaria note list.",
inputSchema: {
type: "object",
properties: {
@@ -28134,7 +28134,7 @@ async function handleGetNote(args) {
function handleOpenNote(args) {
broadcastUiAction("vault_changed", { path: args.path });
broadcastUiAction("open_tab", { path: args.path });
- return { content: [{ type: "text", text: `Opening ${args.path} in Laputa` }] };
+ return { content: [{ type: "text", text: `Opening ${args.path} in Tolaria` }] };
}
function handleHighlightEditor(args) {
broadcastUiAction("highlight", { element: args.element, path: args.path });
@@ -28145,7 +28145,7 @@ function handleRefreshVault(args) {
return { content: [{ type: "text", text: "Vault refresh triggered" }] };
}
var server = new Server(
- { name: "laputa-mcp-server", version: "0.3.0" },
+ { name: "tolaria-mcp-server", version: "0.3.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -28169,7 +28169,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
- console.error(`Laputa MCP server running (vault: ${VAULT_PATH})`);
+ console.error(`Tolaria MCP server running (vault: ${VAULT_PATH})`);
}
main().catch(console.error);
/*! Bundled license information:
diff --git a/src-tauri/gen/apple/laputa.xcodeproj/project.pbxproj b/src-tauri/gen/apple/laputa.xcodeproj/project.pbxproj
index 24f39fc3..d236f984 100644
--- a/src-tauri/gen/apple/laputa.xcodeproj/project.pbxproj
+++ b/src-tauri/gen/apple/laputa.xcodeproj/project.pbxproj
@@ -500,8 +500,8 @@
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
- PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.laputa;
- PRODUCT_NAME = "laputa";
+ PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.tolaria;
+ PRODUCT_NAME = Tolaria;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64;
@@ -531,8 +531,8 @@
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
- PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.laputa;
- PRODUCT_NAME = "laputa";
+ PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.tolaria;
+ PRODUCT_NAME = Tolaria;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64;
diff --git a/src-tauri/gen/apple/project.yml b/src-tauri/gen/apple/project.yml
index b4ae8ab7..aea6fa23 100644
--- a/src-tauri/gen/apple/project.yml
+++ b/src-tauri/gen/apple/project.yml
@@ -1,6 +1,6 @@
-name: laputa
+name: Tolaria
options:
- bundleIdPrefix: club.refactoring.laputa
+ bundleIdPrefix: club.refactoring.tolaria
deploymentTarget:
iOS: 14.0
fileGroups: [../../src]
@@ -10,8 +10,8 @@ configs:
settingGroups:
app:
base:
- PRODUCT_NAME: laputa
- PRODUCT_BUNDLE_IDENTIFIER: club.refactoring.laputa
+ PRODUCT_NAME: Tolaria
+ PRODUCT_BUNDLE_IDENTIFIER: club.refactoring.tolaria
targetTemplates:
app:
type: application
@@ -85,4 +85,4 @@ targets:
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a
- - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a
\ No newline at end of file
+ - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a
diff --git a/src-tauri/src/claude_cli.rs b/src-tauri/src/claude_cli.rs
index 5b9cf410..f3b0bf44 100644
--- a/src-tauri/src/claude_cli.rs
+++ b/src-tauri/src/claude_cli.rs
@@ -208,7 +208,7 @@ fn build_mcp_config(vault_path: &str) -> Result {
let index_js = server_dir.join("index.js");
let config = serde_json::json!({
"mcpServers": {
- "laputa": {
+ "tolaria": {
"command": "node",
"args": [index_js.to_string_lossy()],
"env": { "VAULT_PATH": vault_path }
@@ -508,9 +508,9 @@ mod tests {
fn build_mcp_config_is_valid_json() {
if let Ok(config_str) = build_mcp_config("/tmp/test-vault") {
let parsed: serde_json::Value = serde_json::from_str(&config_str).unwrap();
- assert!(parsed["mcpServers"]["laputa"]["command"].is_string());
+ assert!(parsed["mcpServers"]["tolaria"]["command"].is_string());
assert_eq!(
- parsed["mcpServers"]["laputa"]["env"]["VAULT_PATH"],
+ parsed["mcpServers"]["tolaria"]["env"]["VAULT_PATH"],
"/tmp/test-vault"
);
}
diff --git a/src-tauri/src/git/mod.rs b/src-tauri/src/git/mod.rs
index 5bc6881d..2fa4b684 100644
--- a/src-tauri/src/git/mod.rs
+++ b/src-tauri/src/git/mod.rs
@@ -35,7 +35,7 @@ pub struct GitCommit {
pub date: i64,
}
-const DEFAULT_GITIGNORE: &str = "# Laputa app files (machine-specific, never commit)\n\
+const DEFAULT_GITIGNORE: &str = "# Tolaria app files (machine-specific, never commit)\n\
.laputa/settings.json\n\
\n\
# macOS\n\
@@ -101,7 +101,7 @@ fn run_git(dir: &Path, args: &[&str]) -> Result<(), String> {
/// Set local user.name and user.email if not already configured.
fn ensure_author_config(dir: &Path) -> Result<(), String> {
- for (key, fallback) in [("user.name", "Laputa"), ("user.email", "vault@laputa.app")] {
+ for (key, fallback) in [("user.name", "Tolaria"), ("user.email", "vault@tolaria.app")] {
let check = Command::new("git")
.args(["config", key])
.current_dir(dir)
diff --git a/src-tauri/src/github/api.rs b/src-tauri/src/github/api.rs
index 52606208..9e14b5db 100644
--- a/src-tauri/src/github/api.rs
+++ b/src-tauri/src/github/api.rs
@@ -22,7 +22,7 @@ async fn github_list_repos_with_base(
.get(&url)
.header("Authorization", format!("Bearer {}", token))
.header("Accept", "application/vnd.github+json")
- .header("User-Agent", "Laputa-App")
+ .header("User-Agent", "Tolaria-App")
.header("X-GitHub-Api-Version", "2022-11-28")
.send()
.await
@@ -74,14 +74,14 @@ async fn github_create_repo_with_base(
"name": name,
"private": private,
"auto_init": true,
- "description": "Laputa vault"
+ "description": "Tolaria vault"
});
let response = client
.post(format!("{}/user/repos", api_base))
.header("Authorization", format!("Bearer {}", token))
.header("Accept", "application/vnd.github+json")
- .header("User-Agent", "Laputa-App")
+ .header("User-Agent", "Tolaria-App")
.header("X-GitHub-Api-Version", "2022-11-28")
.json(&body)
.send()
@@ -114,7 +114,7 @@ async fn github_get_user_with_base(token: &str, api_base: &str) -> Result Result R
// Also configure git user if not set
let _ = Command::new("git")
- .args(["config", "user.email", "laputa@app.local"])
+ .args(["config", "user.email", "tolaria@app.local"])
.current_dir(vault)
.output();
let _ = Command::new("git")
- .args(["config", "user.name", "Laputa App"])
+ .args(["config", "user.name", "Tolaria App"])
.current_dir(vault)
.output();
@@ -266,12 +266,12 @@ mod tests {
.output()
.unwrap();
StdCommand::new("git")
- .args(["config", "user.email", "laputa@app.local"])
+ .args(["config", "user.email", "tolaria@app.local"])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
- .args(["config", "user.name", "Laputa App"])
+ .args(["config", "user.name", "Tolaria App"])
.current_dir(path)
.output()
.unwrap();
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 9ed013f9..5cbd408a 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -48,7 +48,7 @@ fn run_startup_tasks() {
// Seed AGENTS.md and config.md at vault root if missing
vault::seed_config_files(vp_str);
- // Register Laputa MCP server in Claude Code and Cursor configs
+ // Register Tolaria MCP server in Claude Code and Cursor configs
match mcp::register_mcp(vp_str) {
Ok(status) => log::info!("MCP registration: {status}"),
Err(e) => log::warn!("MCP registration failed: {e}"),
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 3e4e0df1..312d1c82 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
- laputa_lib::run();
+ tolaria_lib::run();
}
diff --git a/src-tauri/src/mcp.rs b/src-tauri/src/mcp.rs
index 4348f78e..bd3b78f5 100644
--- a/src-tauri/src/mcp.rs
+++ b/src-tauri/src/mcp.rs
@@ -2,6 +2,9 @@ use serde::Serialize;
use std::path::{Path, PathBuf};
use std::process::{Child, Command};
+const MCP_SERVER_NAME: &str = "tolaria";
+const LEGACY_MCP_SERVER_NAME: &str = "laputa";
+
/// Status of the MCP server installation.
#[derive(Debug, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
@@ -20,11 +23,41 @@ pub(crate) fn find_node() -> Result {
.arg("node")
.output()
.map_err(|e| format!("Failed to run `which node`: {e}"))?;
- if !output.status.success() {
- return Err("node not found in PATH".into());
+ if output.status.success() {
+ let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
+ if !path.is_empty() {
+ return Ok(PathBuf::from(path));
+ }
}
- let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
- Ok(PathBuf::from(path))
+
+ if let Some(path) = fallback_node_path() {
+ return Ok(path);
+ }
+
+ Err("node not found in PATH or common install locations".into())
+}
+
+fn fallback_node_path() -> Option {
+ let mut candidates = vec![
+ PathBuf::from("/opt/homebrew/bin/node"),
+ PathBuf::from("/usr/local/bin/node"),
+ ];
+
+ if let Some(home) = dirs::home_dir() {
+ candidates.push(home.join(".volta").join("bin").join("node"));
+
+ let nvm_dir = home.join(".nvm").join("versions").join("node");
+ if let Ok(entries) = std::fs::read_dir(nvm_dir) {
+ let mut versions = entries
+ .filter_map(|entry| entry.ok().map(|entry| entry.path()))
+ .collect::>();
+ versions.sort();
+ versions.reverse();
+ candidates.extend(versions.into_iter().map(|version| version.join("bin").join("node")));
+ }
+ }
+
+ candidates.into_iter().find(|path| path.is_file())
}
/// Resolve the path to `mcp-server/`.
@@ -102,7 +135,7 @@ fn register_mcp_to_configs(entry: &serde_json::Value, config_paths: &[PathBuf])
status.to_string()
}
-/// Register Laputa as an MCP server in Claude Code and Cursor config files.
+/// Register Tolaria as an MCP server in Claude Code and Cursor config files.
pub fn register_mcp(vault_path: &str) -> Result {
let server_dir = mcp_server_dir()?;
let index_js = server_dir.join("index.js").to_string_lossy().into_owned();
@@ -120,7 +153,7 @@ pub fn register_mcp(vault_path: &str) -> Result {
Ok(register_mcp_to_configs(&entry, &configs))
}
-/// Insert or update the "laputa" entry in an MCP config file.
+/// Insert or update the Tolaria entry in an MCP config file.
fn upsert_mcp_config(config_path: &Path, entry: &serde_json::Value) -> Result {
if let Some(parent) = config_path.parent() {
std::fs::create_dir_all(parent)
@@ -142,12 +175,14 @@ fn upsert_mcp_config(config_path: &Path, entry: &serde_json::Value) -> Result Result McpStatus {
@@ -187,10 +222,15 @@ pub fn check_mcp_status() -> McpStatus {
Err(_) => return McpStatus::NotInstalled,
};
- let entry = &config["mcpServers"]["laputa"];
- if entry.is_null() {
+ let Some(servers) = config.get("mcpServers").and_then(|value| value.as_object()) else {
return McpStatus::NotInstalled;
- }
+ };
+ let Some(entry) = servers
+ .get(MCP_SERVER_NAME)
+ .or_else(|| servers.get(LEGACY_MCP_SERVER_NAME))
+ else {
+ return McpStatus::NotInstalled;
+ };
// Verify the referenced index.js actually exists on disk
if let Some(index_js) = entry["args"]
@@ -231,9 +271,9 @@ mod tests {
let raw = std::fs::read_to_string(&config_path).unwrap();
let config: serde_json::Value = serde_json::from_str(&raw).unwrap();
- assert_eq!(config["mcpServers"]["laputa"]["args"][0], "/test/index.js");
+ assert_eq!(config["mcpServers"][MCP_SERVER_NAME]["args"][0], "/test/index.js");
assert_eq!(
- config["mcpServers"]["laputa"]["env"]["VAULT_PATH"],
+ config["mcpServers"][MCP_SERVER_NAME]["env"]["VAULT_PATH"],
"/test/vault"
);
}
@@ -253,11 +293,37 @@ mod tests {
let raw = std::fs::read_to_string(&config_path).unwrap();
let config: serde_json::Value = serde_json::from_str(&raw).unwrap();
assert_eq!(
- config["mcpServers"]["laputa"]["env"]["VAULT_PATH"],
+ config["mcpServers"][MCP_SERVER_NAME]["env"]["VAULT_PATH"],
"/vault/v2"
);
}
+ #[test]
+ fn upsert_migrates_legacy_server_name() {
+ let tmp = tempfile::tempdir().unwrap();
+ let config_path = tmp.path().join("mcp.json");
+
+ let existing = serde_json::json!({
+ "mcpServers": {
+ "laputa": {
+ "command": "node",
+ "args": ["/old/index.js"],
+ "env": { "VAULT_PATH": "/old" }
+ }
+ }
+ });
+ std::fs::write(&config_path, serde_json::to_string(&existing).unwrap()).unwrap();
+
+ let entry = build_mcp_entry("/test/index.js", "/vault");
+ let was_update = upsert_mcp_config(&config_path, &entry).unwrap();
+ assert!(was_update);
+
+ let raw = std::fs::read_to_string(&config_path).unwrap();
+ let config: serde_json::Value = serde_json::from_str(&raw).unwrap();
+ assert!(config["mcpServers"][LEGACY_MCP_SERVER_NAME].is_null());
+ assert_eq!(config["mcpServers"][MCP_SERVER_NAME]["args"][0], "/test/index.js");
+ }
+
#[test]
fn upsert_preserves_other_servers() {
let tmp = tempfile::tempdir().unwrap();
@@ -276,7 +342,7 @@ mod tests {
let raw = std::fs::read_to_string(&config_path).unwrap();
let config: serde_json::Value = serde_json::from_str(&raw).unwrap();
assert!(config["mcpServers"]["other-server"].is_object());
- assert!(config["mcpServers"]["laputa"].is_object());
+ assert!(config["mcpServers"][MCP_SERVER_NAME].is_object());
}
#[test]
@@ -358,7 +424,7 @@ mod tests {
let raw = std::fs::read_to_string(&claude_cfg).unwrap();
let config: serde_json::Value = serde_json::from_str(&raw).unwrap();
- assert_eq!(config["mcpServers"]["laputa"]["args"][0], "/test/index.js");
+ assert_eq!(config["mcpServers"][MCP_SERVER_NAME]["args"][0], "/test/index.js");
}
#[test]
fn upsert_returns_error_for_invalid_json() {
diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs
index d4a422eb..3d51d324 100644
--- a/src-tauri/src/menu.rs
+++ b/src-tauri/src/menu.rs
@@ -128,7 +128,7 @@ fn build_app_menu(app: &App) -> MenuResult {
.id(APP_CHECK_FOR_UPDATES)
.build(app)?;
- Ok(SubmenuBuilder::new(app, "Laputa")
+ Ok(SubmenuBuilder::new(app, "Tolaria")
.about(None)
.separator()
.item(&check_updates_item)
diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs
index 4731b939..08932b15 100644
--- a/src-tauri/src/settings.rs
+++ b/src-tauri/src/settings.rs
@@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
+const APP_CONFIG_DIR: &str = "com.tolaria.app";
+const LEGACY_APP_CONFIG_DIR: &str = "com.laputa.app";
+
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Settings {
pub github_token: Option,
@@ -14,12 +17,33 @@ pub struct Settings {
pub release_channel: Option,
}
-fn settings_path() -> Result {
+fn app_config_dir() -> Result {
dirs::config_dir()
- .map(|d| d.join("com.laputa.app").join("settings.json"))
.ok_or_else(|| "Could not determine config directory".to_string())
}
+fn preferred_app_config_path(file_name: &str) -> Result {
+ Ok(app_config_dir()?.join(APP_CONFIG_DIR).join(file_name))
+}
+
+fn resolve_existing_or_preferred_app_config_path(file_name: &str) -> Result {
+ let preferred = preferred_app_config_path(file_name)?;
+ if preferred.exists() {
+ return Ok(preferred);
+ }
+
+ let legacy = app_config_dir()?.join(LEGACY_APP_CONFIG_DIR).join(file_name);
+ if legacy.exists() {
+ return Ok(legacy);
+ }
+
+ Ok(preferred)
+}
+
+fn settings_path() -> Result {
+ resolve_existing_or_preferred_app_config_path("settings.json")
+}
+
fn get_settings_at(path: &PathBuf) -> Result {
if !path.exists() {
return Ok(Settings::default());
@@ -69,13 +93,11 @@ pub fn get_settings() -> Result {
}
pub fn save_settings(settings: Settings) -> Result<(), String> {
- save_settings_at(&settings_path()?, settings)
+ save_settings_at(&preferred_app_config_path("settings.json")?, settings)
}
fn last_vault_file() -> Result {
- dirs::config_dir()
- .map(|d| d.join("com.laputa.app").join("last-vault.txt"))
- .ok_or_else(|| "Could not determine config directory".to_string())
+ resolve_existing_or_preferred_app_config_path("last-vault.txt")
}
fn get_last_vault_at(path: &PathBuf) -> Option {
@@ -99,7 +121,7 @@ pub fn get_last_vault() -> Option {
}
pub fn set_last_vault(vault_path: &str) -> Result<(), String> {
- set_last_vault_at(&last_vault_file()?, vault_path)
+ set_last_vault_at(&preferred_app_config_path("last-vault.txt")?, vault_path)
}
#[cfg(test)]
@@ -251,7 +273,16 @@ mod tests {
fn test_settings_path_returns_ok() {
let result = settings_path();
assert!(result.is_ok());
- assert!(result.unwrap().to_str().unwrap().contains("com.laputa.app"));
+ let path = result.unwrap();
+ let path = path.to_str().unwrap();
+ assert!(path.contains("com.tolaria.app") || path.contains("com.laputa.app"));
+ }
+
+ #[test]
+ fn test_preferred_settings_path_uses_tolaria_namespace() {
+ let result = preferred_app_config_path("settings.json");
+ assert!(result.is_ok());
+ assert!(result.unwrap().to_str().unwrap().contains("com.tolaria.app"));
}
#[test]
diff --git a/src-tauri/src/vault/config_seed.rs b/src-tauri/src/vault/config_seed.rs
index fd08ab2d..955ca4e3 100644
--- a/src-tauri/src/vault/config_seed.rs
+++ b/src-tauri/src/vault/config_seed.rs
@@ -159,7 +159,7 @@ mod tests {
assert!(vault.join("AGENTS.md").exists());
let content = fs::read_to_string(vault.join("AGENTS.md")).unwrap();
- assert!(content.contains("Laputa Vault"));
+ assert!(content.contains("Tolaria Vault"));
// Must NOT create config/ directory
assert!(!vault.join("config").exists());
}
@@ -205,7 +205,7 @@ mod tests {
seed_config_files(vault.to_str().unwrap());
let content = fs::read_to_string(vault.join("AGENTS.md")).unwrap();
- assert!(content.contains("Laputa Vault"));
+ assert!(content.contains("Tolaria Vault"));
}
#[test]
@@ -272,7 +272,7 @@ mod tests {
assert!(vault.join("AGENTS.md").exists());
let root = fs::read_to_string(vault.join("AGENTS.md")).unwrap();
- assert!(root.contains("Laputa Vault"));
+ assert!(root.contains("Tolaria Vault"));
}
#[test]
@@ -305,7 +305,7 @@ mod tests {
assert!(!vault.join("config").exists());
let agents = fs::read_to_string(vault.join("AGENTS.md")).unwrap();
- assert!(agents.contains("Laputa Vault"));
+ assert!(agents.contains("Tolaria Vault"));
}
#[test]
diff --git a/src-tauri/src/vault/getting_started.rs b/src-tauri/src/vault/getting_started.rs
index ecb9745a..5bb89c47 100644
--- a/src-tauri/src/vault/getting_started.rs
+++ b/src-tauri/src/vault/getting_started.rs
@@ -19,9 +19,9 @@ pub fn vault_exists(path: &str) -> bool {
/// Default AGENTS.md content — vault instructions for AI agents.
/// Describes Laputa vault mechanics only; no vault-specific structure.
/// The vault scanner will pick this up as a regular entry.
-pub(super) const AGENTS_MD: &str = r##"# AGENTS.md — Laputa Vault
+pub(super) const AGENTS_MD: &str = r##"# AGENTS.md — Tolaria Vault
-This is a [Laputa](https://github.com/refactoringhq/laputa-app) vault — a folder of markdown files with YAML frontmatter forming a personal knowledge graph.
+This is a [Tolaria](https://github.com/refactoringhq/tolaria) vault — a folder of markdown files with YAML frontmatter forming a personal knowledge graph.
## Note structure
@@ -120,7 +120,8 @@ fn create_getting_started_vault_from_repo(
}
fn getting_started_repo_url() -> String {
- std::env::var("LAPUTA_GETTING_STARTED_REPO_URL")
+ std::env::var("TOLARIA_GETTING_STARTED_REPO_URL")
+ .or_else(|_| std::env::var("LAPUTA_GETTING_STARTED_REPO_URL"))
.unwrap_or_else(|_| GETTING_STARTED_REPO_URL.to_string())
}
@@ -143,7 +144,7 @@ mod tests {
fs::create_dir_all(path.join("views")).unwrap();
fs::write(
path.join("welcome.md"),
- "# Welcome to Laputa\n\nThis is the starter vault.\n",
+ "# Welcome to Tolaria\n\nThis is the starter vault.\n",
)
.unwrap();
fs::write(
@@ -158,12 +159,12 @@ mod tests {
.output()
.unwrap();
StdCommand::new("git")
- .args(["config", "user.email", "laputa@app.local"])
+ .args(["config", "user.email", "tolaria@app.local"])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
- .args(["config", "user.name", "Laputa App"])
+ .args(["config", "user.name", "Tolaria App"])
.current_dir(path)
.output()
.unwrap();
diff --git a/src-tauri/src/vault_list.rs b/src-tauri/src/vault_list.rs
index ef4e37ad..ce8c70df 100644
--- a/src-tauri/src/vault_list.rs
+++ b/src-tauri/src/vault_list.rs
@@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
+const APP_CONFIG_DIR: &str = "com.tolaria.app";
+const LEGACY_APP_CONFIG_DIR: &str = "com.laputa.app";
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VaultEntry {
pub label: String,
@@ -16,12 +19,33 @@ pub struct VaultList {
pub hidden_defaults: Vec,
}
-fn vault_list_path() -> Result {
+fn app_config_dir() -> Result {
dirs::config_dir()
- .map(|d| d.join("com.laputa.app").join("vaults.json"))
.ok_or_else(|| "Could not determine config directory".to_string())
}
+fn preferred_app_config_path(file_name: &str) -> Result {
+ Ok(app_config_dir()?.join(APP_CONFIG_DIR).join(file_name))
+}
+
+fn resolve_existing_or_preferred_app_config_path(file_name: &str) -> Result {
+ let preferred = preferred_app_config_path(file_name)?;
+ if preferred.exists() {
+ return Ok(preferred);
+ }
+
+ let legacy = app_config_dir()?.join(LEGACY_APP_CONFIG_DIR).join(file_name);
+ if legacy.exists() {
+ return Ok(legacy);
+ }
+
+ Ok(preferred)
+}
+
+fn vault_list_path() -> Result {
+ resolve_existing_or_preferred_app_config_path("vaults.json")
+}
+
fn load_at(path: &PathBuf) -> Result {
if !path.exists() {
return Ok(VaultList::default());
@@ -46,7 +70,7 @@ pub fn load_vault_list() -> Result {
}
pub fn save_vault_list(list: &VaultList) -> Result<(), String> {
- save_at(&vault_list_path()?, list)
+ save_at(&preferred_app_config_path("vaults.json")?, list)
}
#[cfg(test)]
@@ -131,7 +155,16 @@ mod tests {
fn vault_list_path_returns_ok() {
let result = vault_list_path();
assert!(result.is_ok());
- assert!(result.unwrap().to_str().unwrap().contains("com.laputa.app"));
+ let path = result.unwrap();
+ let path = path.to_str().unwrap();
+ assert!(path.contains("com.tolaria.app") || path.contains("com.laputa.app"));
+ }
+
+ #[test]
+ fn preferred_vault_list_path_uses_tolaria_namespace() {
+ let result = preferred_app_config_path("vaults.json");
+ assert!(result.is_ok());
+ assert!(result.unwrap().to_str().unwrap().contains("com.tolaria.app"));
}
#[test]
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 0a4a19fb..3c999d1c 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,8 +1,8 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
- "productName": "laputa",
+ "productName": "Tolaria",
"version": "0.1.0",
- "identifier": "club.refactoring.laputa",
+ "identifier": "club.refactoring.tolaria",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5202",
@@ -13,7 +13,7 @@
"withGlobalTauri": true,
"windows": [
{
- "title": "Laputa",
+ "title": "Tolaria",
"width": 1400,
"height": 900,
"minWidth": 1200,
@@ -54,9 +54,9 @@
"plugins": {
"updater": {
"endpoints": [
- "https://refactoringhq.github.io/laputa-app/latest.json"
+ "https://refactoringhq.github.io/tolaria/latest.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEE4NkQ5MDI3REVCRkFGNUMKUldSY3I3L2VKNUJ0cU5JRlRZZlp3NGhnU3ZwbkVKeGVvREpmb2sxRVJndHFpVFZPNlArbEE5R1IK"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/App.test.tsx b/src/App.test.tsx
index 868f56a2..89068134 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -128,7 +128,10 @@ describe('App', () => {
vi.clearAllMocks()
// Reset view mode and onboarding state between tests
+ localStorage.removeItem('tolaria-view-mode')
+ localStorage.removeItem('tolaria-view-mode')
localStorage.removeItem('laputa-view-mode')
+ localStorage.removeItem('tolaria_welcome_dismissed')
localStorage.removeItem('laputa_welcome_dismissed')
})
diff --git a/src/components/AiActionCard.tsx b/src/components/AiActionCard.tsx
index 5a3c2d4a..817844df 100644
--- a/src/components/AiActionCard.tsx
+++ b/src/components/AiActionCard.tsx
@@ -31,7 +31,7 @@ const TOOL_ICON_MAP: Record = {
Read: (s) => ,
Glob: (s) => ,
Grep: (s) => ,
- // Laputa MCP tools
+ // Tolaria MCP tools
search_notes: (s) => ,
get_vault_context: (s) => ,
get_note: (s) => ,
@@ -100,7 +100,7 @@ function DetailBlock({ label, content, isError }: {
)
}
-/** Whether this tool is a Laputa UI-only tool (lighter styling). */
+/** Whether this tool is a Tolaria UI-only tool (lighter styling). */
function isUiOnlyTool(tool: string): boolean {
return tool === 'open_note'
}
diff --git a/src/components/FeedbackDialog.test.tsx b/src/components/FeedbackDialog.test.tsx
index 1065f21c..1eacdfaa 100644
--- a/src/components/FeedbackDialog.test.tsx
+++ b/src/components/FeedbackDialog.test.tsx
@@ -1,7 +1,7 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { FeedbackDialog } from './FeedbackDialog'
-import { LAPUTA_GITHUB_ISSUES_URL } from '../constants/feedback'
+import { TOLARIA_GITHUB_ISSUES_URL } from '../constants/feedback'
vi.mock('../utils/url', () => ({
openExternalUrl: vi.fn().mockResolvedValue(undefined),
@@ -32,7 +32,7 @@ describe('FeedbackDialog', () => {
fireEvent.click(screen.getByRole('button', { name: 'Go to Issues' }))
- await waitFor(() => expect(openExternalUrl).toHaveBeenCalledWith(LAPUTA_GITHUB_ISSUES_URL))
+ await waitFor(() => expect(openExternalUrl).toHaveBeenCalledWith(TOLARIA_GITHUB_ISSUES_URL))
expect(onClose).not.toHaveBeenCalled()
expect(screen.getByTestId('feedback-dialog')).toBeInTheDocument()
})
diff --git a/src/components/FeedbackDialog.tsx b/src/components/FeedbackDialog.tsx
index 1d2ebb79..57c8a95b 100644
--- a/src/components/FeedbackDialog.tsx
+++ b/src/components/FeedbackDialog.tsx
@@ -8,7 +8,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
-import { LAPUTA_GITHUB_ISSUES_URL } from '../constants/feedback'
+import { TOLARIA_GITHUB_ISSUES_URL } from '../constants/feedback'
import { openExternalUrl } from '../utils/url'
interface FeedbackDialogProps {
@@ -18,7 +18,7 @@ interface FeedbackDialogProps {
export function FeedbackDialog({ open, onClose }: FeedbackDialogProps) {
const handleOpenIssues = () => {
- void openExternalUrl(LAPUTA_GITHUB_ISSUES_URL)
+ void openExternalUrl(TOLARIA_GITHUB_ISSUES_URL)
}
return (
diff --git a/src/components/GitRequiredModal.tsx b/src/components/GitRequiredModal.tsx
index 17f2255d..19822a22 100644
--- a/src/components/GitRequiredModal.tsx
+++ b/src/components/GitRequiredModal.tsx
@@ -27,7 +27,7 @@ export function GitRequiredModal({ onCreateRepo, onChooseVault }: GitRequiredMod
Git repository required
- Laputa uses a git repository to track changes, detect moved files, and keep your vault safe.
+ Tolaria uses a git repository to track changes, detect moved files, and keep your vault safe.
We'll create a local repo — no remote needed.