@sideboard-ai/core 0.1.9

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.
@@ -0,0 +1,304 @@
1
+ // src/store/paths.ts
2
+ import { basename } from "path";
3
+ import { homedir as homedir2 } from "os";
4
+ import { join as join2 } from "path";
5
+ import { mkdirSync } from "fs";
6
+
7
+ // src/hook/settings.ts
8
+ import { existsSync, readFileSync } from "fs";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+ import { parse as parseToml } from "smol-toml";
12
+ function expandHome(path) {
13
+ if (path.startsWith("~/") || path === "~") {
14
+ return join(homedir(), path.slice(2));
15
+ }
16
+ return path;
17
+ }
18
+ function parseAvailableIn(raw) {
19
+ if (!Array.isArray(raw)) return void 0;
20
+ const out = [];
21
+ for (const item of raw) {
22
+ if (item === "local" || item === "cloud") out.push(item);
23
+ }
24
+ return out.length ? out : void 0;
25
+ }
26
+ function parseSettingsFile(path, source) {
27
+ if (!existsSync(path)) return null;
28
+ const raw = readFileSync(path, "utf8");
29
+ const data = parseToml(raw);
30
+ const scripts = data.scripts ?? {};
31
+ const setup = typeof scripts.setup === "string" ? scripts.setup : void 0;
32
+ const archive = typeof scripts.archive === "string" ? scripts.archive : void 0;
33
+ const runMode = "run_mode" in scripts ? scripts.run_mode === "nonconcurrent" ? "nonconcurrent" : "concurrent" : void 0;
34
+ const filesToCopy = [];
35
+ const files = data.files;
36
+ if (Array.isArray(files?.copy)) {
37
+ filesToCopy.push(...files.copy.map(String));
38
+ }
39
+ const copySection = data["files-to-copy"];
40
+ if (Array.isArray(copySection?.paths)) {
41
+ filesToCopy.push(...copySection.paths.map(String));
42
+ }
43
+ const fileIncludeGlobs = [];
44
+ if (Array.isArray(data.file_include_globs)) {
45
+ fileIncludeGlobs.push(...data.file_include_globs.map(String));
46
+ }
47
+ const filesGlobs = files;
48
+ if (Array.isArray(filesGlobs?.include)) {
49
+ fileIncludeGlobs.push(...filesGlobs.include.map(String));
50
+ }
51
+ if (Array.isArray(filesGlobs?.include_globs)) {
52
+ fileIncludeGlobs.push(...filesGlobs.include_globs.map(String));
53
+ }
54
+ const runScripts = [];
55
+ const run = scripts.run;
56
+ if (typeof run === "string" && run.trim()) {
57
+ runScripts.push({ name: "dev", command: run, default: true });
58
+ } else if (run && typeof run === "object") {
59
+ for (const [name, value] of Object.entries(run)) {
60
+ if (value && typeof value.command === "string") {
61
+ runScripts.push({
62
+ name,
63
+ command: value.command,
64
+ default: Boolean(value.default),
65
+ icon: typeof value.icon === "string" ? value.icon : void 0,
66
+ availableIn: parseAvailableIn(value.available_in)
67
+ });
68
+ }
69
+ }
70
+ }
71
+ const worktrees = data.worktrees;
72
+ const worktreesRoot2 = typeof worktrees?.root === "string" ? expandHome(worktrees.root) : void 0;
73
+ const editor = typeof data.editor === "string" ? String(data.editor) : void 0;
74
+ const promptsRaw = data.prompts ?? {};
75
+ const prompts = {
76
+ renameBranch: typeof promptsRaw.rename_branch === "string" ? promptsRaw.rename_branch : void 0,
77
+ createPr: typeof promptsRaw.create_pr === "string" ? promptsRaw.create_pr : void 0,
78
+ general: typeof promptsRaw.general === "string" ? promptsRaw.general : void 0
79
+ };
80
+ const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
81
+ return {
82
+ source,
83
+ setup,
84
+ archive,
85
+ runMode,
86
+ filesToCopy: filesToCopy.length ? filesToCopy : void 0,
87
+ fileIncludeGlobs: fileIncludeGlobs.length ? fileIncludeGlobs : void 0,
88
+ runScripts,
89
+ worktreesRoot: worktreesRoot2,
90
+ editor,
91
+ prompts: hasPrompts ? prompts : void 0
92
+ };
93
+ }
94
+ function mergeRunScripts(base, overlay) {
95
+ const byName = /* @__PURE__ */ new Map();
96
+ for (const s of base) byName.set(s.name, s);
97
+ for (const s of overlay) byName.set(s.name, s);
98
+ return [...byName.values()];
99
+ }
100
+ function mergeSettings(base, overlay) {
101
+ if (!base) return overlay;
102
+ if (!overlay) return base;
103
+ const prompts = {
104
+ renameBranch: overlay.prompts?.renameBranch ?? base.prompts?.renameBranch,
105
+ createPr: overlay.prompts?.createPr ?? base.prompts?.createPr,
106
+ general: overlay.prompts?.general ?? base.prompts?.general
107
+ };
108
+ const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
109
+ return {
110
+ source: overlay.source,
111
+ setup: overlay.setup ?? base.setup,
112
+ archive: overlay.archive ?? base.archive,
113
+ runMode: overlay.runMode ?? base.runMode,
114
+ filesToCopy: overlay.filesToCopy ?? base.filesToCopy,
115
+ fileIncludeGlobs: overlay.fileIncludeGlobs ?? base.fileIncludeGlobs,
116
+ runScripts: mergeRunScripts(base.runScripts, overlay.runScripts),
117
+ worktreesRoot: overlay.worktreesRoot ?? base.worktreesRoot,
118
+ editor: overlay.editor ?? base.editor,
119
+ prompts: hasPrompts ? prompts : void 0
120
+ };
121
+ }
122
+ function finalizeSettings(parsed) {
123
+ if (!parsed) return null;
124
+ return {
125
+ ...parsed,
126
+ runMode: parsed.runMode ?? "concurrent",
127
+ runScripts: parsed.runScripts
128
+ };
129
+ }
130
+ function familyFiles(rootPath, source) {
131
+ const dir = join(rootPath, `.${source}`);
132
+ return {
133
+ toml: join(dir, "settings.toml"),
134
+ local: join(dir, "settings.local.toml")
135
+ };
136
+ }
137
+ function familyExists(rootPath, source) {
138
+ const files = familyFiles(rootPath, source);
139
+ return existsSync(files.toml) || existsSync(files.local);
140
+ }
141
+ function detectFamily(rootPath) {
142
+ if (familyExists(rootPath, "sideboard")) return "sideboard";
143
+ if (familyExists(rootPath, "conductor")) return "conductor";
144
+ return null;
145
+ }
146
+ function loadCommittedToml(rootPath, source) {
147
+ return parseSettingsFile(familyFiles(rootPath, source).toml, source);
148
+ }
149
+ function loadLocalToml(rootPath, source) {
150
+ return parseSettingsFile(familyFiles(rootPath, source).local, source);
151
+ }
152
+ function loadAnyLocal(rootPath) {
153
+ return mergeSettings(
154
+ loadLocalToml(rootPath, "conductor"),
155
+ loadLocalToml(rootPath, "sideboard")
156
+ );
157
+ }
158
+ function normPath(p) {
159
+ return p.replace(/\/+$/, "");
160
+ }
161
+ function loadRepoSettings(repoPath) {
162
+ const family = detectFamily(repoPath);
163
+ if (!family) return null;
164
+ const merged = mergeSettings(
165
+ loadCommittedToml(repoPath, family),
166
+ loadLocalToml(repoPath, family)
167
+ );
168
+ return finalizeSettings(merged);
169
+ }
170
+ function loadWorkspaceSettings(worktreePath, repoPath) {
171
+ const wtFamily = detectFamily(worktreePath);
172
+ const repoFamily = repoPath && normPath(repoPath) !== normPath(worktreePath) ? detectFamily(repoPath) : null;
173
+ const wtToml = wtFamily ? loadCommittedToml(worktreePath, wtFamily) : null;
174
+ const repoToml = repoPath && repoFamily ? loadCommittedToml(repoPath, repoFamily) : null;
175
+ let merged = wtToml ?? repoToml;
176
+ if (repoPath && normPath(repoPath) !== normPath(worktreePath)) {
177
+ merged = mergeSettings(merged, loadAnyLocal(repoPath));
178
+ }
179
+ merged = mergeSettings(merged, loadAnyLocal(worktreePath));
180
+ return finalizeSettings(merged);
181
+ }
182
+ function loadConductorSettings(repoPath) {
183
+ return loadRepoSettings(repoPath);
184
+ }
185
+ function hasRepoHook(repoPath) {
186
+ return detectFamily(repoPath) !== null;
187
+ }
188
+ function hasWorkspaceHook(worktreePath, repoPath) {
189
+ if (hasRepoHook(worktreePath)) return true;
190
+ if (repoPath && normPath(repoPath) !== normPath(worktreePath) && hasRepoHook(repoPath)) {
191
+ return true;
192
+ }
193
+ return false;
194
+ }
195
+ function hasConductorHook(worktreePath, repoPath) {
196
+ return hasWorkspaceHook(worktreePath, repoPath);
197
+ }
198
+ function settingsSourceLabel(rootPath) {
199
+ const family = detectFamily(rootPath);
200
+ if (!family) return null;
201
+ const files = familyFiles(rootPath, family);
202
+ if (existsSync(files.toml) && existsSync(files.local)) {
203
+ return `.${family}/settings.toml + local`;
204
+ }
205
+ if (existsSync(files.local)) return `.${family}/settings.local.toml`;
206
+ return `.${family}/settings.toml`;
207
+ }
208
+ function workspaceSettingsSourceLabel(worktreePath, repoPath) {
209
+ const wt = settingsSourceLabel(worktreePath);
210
+ if (wt) return `${wt} (worktree)`;
211
+ if (repoPath && normPath(repoPath) !== normPath(worktreePath)) {
212
+ const repo = settingsSourceLabel(repoPath);
213
+ if (repo) return `${repo} (main repo)`;
214
+ }
215
+ return null;
216
+ }
217
+ function getRepoSetupInfo(worktreePath, repoPath) {
218
+ const settings = loadWorkspaceSettings(worktreePath, repoPath);
219
+ return {
220
+ hasConfig: hasWorkspaceHook(worktreePath, repoPath),
221
+ hasSetupScript: Boolean(settings?.setup),
222
+ configLabel: workspaceSettingsSourceLabel(worktreePath, repoPath)
223
+ };
224
+ }
225
+
226
+ // src/store/paths.ts
227
+ function appDataDir() {
228
+ const override = process.env.SIDEBOARD_APP_DATA?.trim();
229
+ const base = override ? override : process.platform === "darwin" ? join2(homedir2(), "Library", "Application Support", "sideboard") : process.platform === "win32" ? join2(process.env.APPDATA ?? join2(homedir2(), "AppData", "Roaming"), "sideboard") : join2(homedir2(), ".local", "share", "sideboard");
230
+ mkdirSync(base, { recursive: true });
231
+ return base;
232
+ }
233
+ function threadsDir() {
234
+ const dir = join2(appDataDir(), "threads");
235
+ mkdirSync(dir, { recursive: true });
236
+ return dir;
237
+ }
238
+ function locksDir() {
239
+ const dir = join2(appDataDir(), "locks");
240
+ mkdirSync(dir, { recursive: true });
241
+ return dir;
242
+ }
243
+ function sideboardHomeDir() {
244
+ const dir = join2(homedir2(), "sideboard");
245
+ mkdirSync(dir, { recursive: true });
246
+ return dir;
247
+ }
248
+ function sideboardReposDir() {
249
+ const dir = join2(sideboardHomeDir(), "repos");
250
+ mkdirSync(dir, { recursive: true });
251
+ return dir;
252
+ }
253
+ function sideboardWorkspacesDir() {
254
+ const dir = join2(sideboardHomeDir(), "workspaces");
255
+ mkdirSync(dir, { recursive: true });
256
+ return dir;
257
+ }
258
+ function repoSlug(repoPath) {
259
+ return basename(repoPath.replace(/\/$/, "")) || "repo";
260
+ }
261
+ function worktreesRoot(repoPath) {
262
+ const settings = loadRepoSettings(repoPath);
263
+ if (settings?.worktreesRoot) {
264
+ mkdirSync(settings.worktreesRoot, { recursive: true });
265
+ return settings.worktreesRoot;
266
+ }
267
+ const dir = join2(sideboardWorkspacesDir(), repoSlug(repoPath));
268
+ mkdirSync(dir, { recursive: true });
269
+ return dir;
270
+ }
271
+ function threadFilePath(id) {
272
+ return join2(threadsDir(), `${id}.json`);
273
+ }
274
+ function threadLockPath(id) {
275
+ return join2(locksDir(), `${id}.lock`);
276
+ }
277
+ function globalAgentCwd() {
278
+ const dir = join2(appDataDir(), "global");
279
+ mkdirSync(dir, { recursive: true });
280
+ return dir;
281
+ }
282
+
283
+ export {
284
+ loadRepoSettings,
285
+ loadWorkspaceSettings,
286
+ loadConductorSettings,
287
+ hasRepoHook,
288
+ hasWorkspaceHook,
289
+ hasConductorHook,
290
+ settingsSourceLabel,
291
+ workspaceSettingsSourceLabel,
292
+ getRepoSetupInfo,
293
+ appDataDir,
294
+ threadsDir,
295
+ locksDir,
296
+ sideboardHomeDir,
297
+ sideboardReposDir,
298
+ sideboardWorkspacesDir,
299
+ repoSlug,
300
+ worktreesRoot,
301
+ threadFilePath,
302
+ threadLockPath,
303
+ globalAgentCwd
304
+ };
@@ -0,0 +1,80 @@
1
+ import {
2
+ isGlobalRepoPath
3
+ } from "./chunk-2M4OHXYX.js";
4
+ import {
5
+ resolveRepoRoot
6
+ } from "./chunk-LL7DTZ5B.js";
7
+ import {
8
+ appDataDir
9
+ } from "./chunk-M37RITA6.js";
10
+
11
+ // src/store/workspaces.ts
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
13
+ import { basename, join } from "path";
14
+ function workspacesFile() {
15
+ return join(appDataDir(), "workspaces.json");
16
+ }
17
+ function readAll() {
18
+ const path = workspacesFile();
19
+ if (!existsSync(path)) return [];
20
+ try {
21
+ const raw = JSON.parse(readFileSync(path, "utf8"));
22
+ return Array.isArray(raw) ? raw : [];
23
+ } catch {
24
+ return [];
25
+ }
26
+ }
27
+ function writeAll(list) {
28
+ mkdirSync(appDataDir(), { recursive: true });
29
+ writeFileSync(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
30
+ }
31
+ function listWorkspaces() {
32
+ return readAll().sort((a, b) => a.name.localeCompare(b.name));
33
+ }
34
+ async function addWorkspace(repoPath) {
35
+ const root = await resolveRepoRoot(repoPath);
36
+ if (!existsSync(root)) throw new Error(`Repo not found: ${root}`);
37
+ const current = readAll();
38
+ const existing = current.find((w) => w.path === root);
39
+ if (existing) return existing;
40
+ const next = {
41
+ path: root,
42
+ name: basename(root),
43
+ addedAt: (/* @__PURE__ */ new Date()).toISOString()
44
+ };
45
+ writeAll([...current, next]);
46
+ return next;
47
+ }
48
+ function removeWorkspace(repoPath) {
49
+ writeAll(readAll().filter((w) => w.path !== repoPath));
50
+ }
51
+ async function ensureWorkspace(repoPath) {
52
+ return addWorkspace(repoPath);
53
+ }
54
+ function syncWorkspacesFromThreads(repoPaths) {
55
+ const current = readAll();
56
+ const byPath = new Map(current.map((w) => [w.path, w]));
57
+ let dirty = false;
58
+ for (const path of repoPaths) {
59
+ if (!path || isGlobalRepoPath(path) || byPath.has(path)) continue;
60
+ if (!existsSync(path)) continue;
61
+ const ws = {
62
+ path,
63
+ name: basename(path),
64
+ addedAt: (/* @__PURE__ */ new Date()).toISOString()
65
+ };
66
+ byPath.set(path, ws);
67
+ dirty = true;
68
+ }
69
+ const next = [...byPath.values()];
70
+ if (dirty) writeAll(next);
71
+ return next.sort((a, b) => a.name.localeCompare(b.name));
72
+ }
73
+
74
+ export {
75
+ listWorkspaces,
76
+ addWorkspace,
77
+ removeWorkspace,
78
+ ensureWorkspace,
79
+ syncWorkspacesFromThreads
80
+ };