@lovelybunch/api 1.0.51 → 1.0.52
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/lib/git-settings.d.ts +7 -0
- package/dist/lib/git-settings.js +84 -0
- package/dist/lib/git.js +6 -2
- package/dist/routes/api/v1/git/index.js +27 -0
- package/package.json +4 -4
- package/static/assets/{index-uuH6d_ep.js → index-Cwq3YsC0.js} +203 -188
- package/static/assets/index-D_1UlOMs.css +33 -0
- package/static/index.html +3 -3
- package/static/assets/index-BVCS1pw6.css +0 -33
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface GitSettings {
|
|
2
|
+
defaultWorktreePath: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function normalizeWorktreePath(input: string): string;
|
|
5
|
+
export declare function loadGitSettings(): Promise<GitSettings>;
|
|
6
|
+
export declare function saveGitSettings(settings: GitSettings): Promise<void>;
|
|
7
|
+
export declare function setDefaultWorktreePath(relativePath: string): Promise<GitSettings>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { findNutDirectory } from './gait-path.js';
|
|
4
|
+
const DEFAULT_SETTINGS = {
|
|
5
|
+
defaultWorktreePath: '../worktrees',
|
|
6
|
+
};
|
|
7
|
+
async function ensureNutDirectory() {
|
|
8
|
+
const nutDir = await findNutDirectory();
|
|
9
|
+
if (nutDir)
|
|
10
|
+
return nutDir;
|
|
11
|
+
const fallback = path.join(process.cwd(), '.nut');
|
|
12
|
+
await fs.mkdir(fallback, { recursive: true });
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
async function getConfigPath() {
|
|
16
|
+
const nutDir = await ensureNutDirectory();
|
|
17
|
+
return path.join(nutDir, 'config.json');
|
|
18
|
+
}
|
|
19
|
+
async function loadProjectConfig() {
|
|
20
|
+
const configPath = await getConfigPath();
|
|
21
|
+
try {
|
|
22
|
+
const raw = await fs.readFile(configPath, 'utf-8');
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error?.code === 'ENOENT') {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
console.warn('[git-settings] Failed to read config.json, using empty config:', error);
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function saveProjectConfig(config) {
|
|
34
|
+
const configPath = await getConfigPath();
|
|
35
|
+
const dir = path.dirname(configPath);
|
|
36
|
+
await fs.mkdir(dir, { recursive: true });
|
|
37
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
export function normalizeWorktreePath(input) {
|
|
40
|
+
const trimmed = input?.trim();
|
|
41
|
+
if (!trimmed) {
|
|
42
|
+
return DEFAULT_SETTINGS.defaultWorktreePath;
|
|
43
|
+
}
|
|
44
|
+
if (path.isAbsolute(trimmed)) {
|
|
45
|
+
throw new Error('Worktree path must be relative to the project root');
|
|
46
|
+
}
|
|
47
|
+
const normalized = path.normalize(trimmed);
|
|
48
|
+
if (!normalized || normalized === '.' || normalized === '') {
|
|
49
|
+
return DEFAULT_SETTINGS.defaultWorktreePath;
|
|
50
|
+
}
|
|
51
|
+
return normalized;
|
|
52
|
+
}
|
|
53
|
+
export async function loadGitSettings() {
|
|
54
|
+
const config = await loadProjectConfig();
|
|
55
|
+
const configuredPath = config.git?.defaultWorktreePath;
|
|
56
|
+
if (!configuredPath) {
|
|
57
|
+
return { ...DEFAULT_SETTINGS };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const normalized = normalizeWorktreePath(configuredPath);
|
|
61
|
+
return { defaultWorktreePath: normalized };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn('[git-settings] Invalid default worktree path in config.json, using default:', error);
|
|
65
|
+
return { ...DEFAULT_SETTINGS };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function saveGitSettings(settings) {
|
|
69
|
+
const config = await loadProjectConfig();
|
|
70
|
+
const normalized = normalizeWorktreePath(settings.defaultWorktreePath);
|
|
71
|
+
const next = {
|
|
72
|
+
...config,
|
|
73
|
+
git: {
|
|
74
|
+
...config.git,
|
|
75
|
+
defaultWorktreePath: normalized,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
await saveProjectConfig(next);
|
|
79
|
+
}
|
|
80
|
+
export async function setDefaultWorktreePath(relativePath) {
|
|
81
|
+
const normalized = normalizeWorktreePath(relativePath);
|
|
82
|
+
await saveGitSettings({ defaultWorktreePath: normalized });
|
|
83
|
+
return { defaultWorktreePath: normalized };
|
|
84
|
+
}
|
package/dist/lib/git.js
CHANGED
|
@@ -2,6 +2,7 @@ import { promisify } from 'util';
|
|
|
2
2
|
import { execFile as _execFile } from 'child_process';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { findGaitDirectory } from './gait-path.js';
|
|
5
|
+
import { loadGitSettings } from './git-settings.js';
|
|
5
6
|
import { promises as fs } from 'fs';
|
|
6
7
|
const execFile = promisify(_execFile);
|
|
7
8
|
// Base directory for worktrees under the repository root
|
|
@@ -16,7 +17,9 @@ export async function getRepoRoot() {
|
|
|
16
17
|
}
|
|
17
18
|
export async function getWorktreesBase() {
|
|
18
19
|
const repoRoot = await getRepoRoot();
|
|
19
|
-
|
|
20
|
+
const settings = await loadGitSettings();
|
|
21
|
+
const relativeBase = settings.defaultWorktreePath || 'worktrees';
|
|
22
|
+
return path.resolve(repoRoot, relativeBase);
|
|
20
23
|
}
|
|
21
24
|
export function sanitizeBranchName(name) {
|
|
22
25
|
if (name.length > 120) {
|
|
@@ -31,7 +34,8 @@ export async function resolveSafeWorktreePath(name) {
|
|
|
31
34
|
const base = await getWorktreesBase();
|
|
32
35
|
const safeName = sanitizeBranchName(name);
|
|
33
36
|
const resolved = path.resolve(base, safeName);
|
|
34
|
-
|
|
37
|
+
const relative = path.relative(base, resolved);
|
|
38
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
35
39
|
throw new Error('Invalid worktree path');
|
|
36
40
|
}
|
|
37
41
|
return resolved;
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { getRepoStatus, listBranches, createBranch, deleteBranch, pushCurrent, pullCurrent, listWorktrees, addWorktree, removeWorktree, commitInWorktree, pushWorktree, pullWorktree, } from '../../../../lib/git.js';
|
|
3
|
+
import { loadGitSettings, setDefaultWorktreePath } from '../../../../lib/git-settings.js';
|
|
3
4
|
const app = new Hono();
|
|
5
|
+
// Settings
|
|
6
|
+
app.get('/settings', async (c) => {
|
|
7
|
+
try {
|
|
8
|
+
const settings = await loadGitSettings();
|
|
9
|
+
return c.json({ success: true, data: settings });
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
return c.json({ success: false, error: { message: e.message || 'Failed to load git settings' } }, 500);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
app.put('/settings', async (c) => {
|
|
16
|
+
try {
|
|
17
|
+
const body = await c.req.json();
|
|
18
|
+
const path = body?.defaultWorktreePath;
|
|
19
|
+
if (typeof path !== 'string' || !path.trim()) {
|
|
20
|
+
return c.json({ success: false, error: { message: 'defaultWorktreePath is required' } }, 400);
|
|
21
|
+
}
|
|
22
|
+
const settings = await setDefaultWorktreePath(path);
|
|
23
|
+
return c.json({ success: true, data: settings });
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
const message = e?.message || 'Failed to update git settings';
|
|
27
|
+
const status = /relative to the project root/i.test(message) ? 400 : 500;
|
|
28
|
+
return c.json({ success: false, error: { message } }, status);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
4
31
|
// Status
|
|
5
32
|
app.get('/status', async (c) => {
|
|
6
33
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovelybunch/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/server-with-static.js",
|
|
6
6
|
"exports": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@hono/node-server": "^1.13.7",
|
|
34
34
|
"@hono/node-ws": "^1.0.6",
|
|
35
|
-
"@lovelybunch/core": "^1.0.
|
|
36
|
-
"@lovelybunch/mcp": "^1.0.
|
|
37
|
-
"@lovelybunch/types": "^1.0.
|
|
35
|
+
"@lovelybunch/core": "^1.0.52",
|
|
36
|
+
"@lovelybunch/mcp": "^1.0.52",
|
|
37
|
+
"@lovelybunch/types": "^1.0.52",
|
|
38
38
|
"dotenv": "^17.2.1",
|
|
39
39
|
"fuse.js": "^7.0.0",
|
|
40
40
|
"gray-matter": "^4.0.3",
|