@spader/dotllm 1.3.2 → 1.3.3

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/src/cli/layout.js CHANGED
@@ -1,8 +1,24 @@
1
1
  // @bun
2
- var __require = import.meta.require;
2
+ // src/cli/theme.ts
3
+ function rgb(r, g, b) {
4
+ return (value) => `\x1B[38;2;${r};${g};${b}m${value}\x1B[39m`;
5
+ }
6
+ var gray = (value) => rgb(value, value, value);
7
+ var defaultTheme = {
8
+ primary: rgb(114, 161, 136),
9
+ link: rgb(114, 140, 212),
10
+ header: gray(128),
11
+ command: rgb(114, 161, 136),
12
+ arg: rgb(161, 212, 212),
13
+ option: rgb(212, 212, 161),
14
+ type: gray(128),
15
+ description: (value) => value,
16
+ dim: gray(128),
17
+ error: rgb(212, 114, 114),
18
+ success: rgb(114, 212, 136)
19
+ };
3
20
 
4
21
  // src/cli/layout.ts
5
- import { defaultTheme as theme } from "@spader/dotllm/cli/theme";
6
22
  var ANSI_RE = /\x1b\[[0-9;]*m/g;
7
23
  function clean(value) {
8
24
  return value.replace(/[\t\n]/g, " ").replace(ANSI_RE, "");
@@ -101,7 +117,7 @@ function table(headers, columns, options = {}) {
101
117
  const available = Math.max(0, maxWidth - gap * (count - 1) - 1);
102
118
  const widths = fitWidths(natural, available, options.flex ?? [], options.noTruncate ?? []);
103
119
  const header = headers.map((title, col) => truncateMiddle(clean(title), widths[col] ?? 0).padEnd(widths[col] ?? 0)).join(" ");
104
- process.stdout.write(`${theme.dim(header)}
120
+ process.stdout.write(`${defaultTheme.dim(header)}
105
121
  `);
106
122
  for (let row = 0;row < visibleRows; row++) {
107
123
  const cells = [];
@@ -118,7 +134,7 @@ function table(headers, columns, options = {}) {
118
134
  `);
119
135
  }
120
136
  if (rows > visibleRows) {
121
- process.stdout.write(`${theme.dim("(...truncated)")}
137
+ process.stdout.write(`${defaultTheme.dim("(...truncated)")}
122
138
  `);
123
139
  }
124
140
  }
@@ -142,6 +158,6 @@ function cols(rows, colorFns) {
142
158
  }
143
159
  }
144
160
  export {
145
- table,
146
- cols
161
+ cols,
162
+ table
147
163
  };
package/src/cli/prompt.js CHANGED
@@ -1,6 +1,4 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
2
  // src/cli/prompt.ts
5
3
  import * as prompts from "@clack/prompts";
6
4
  var Prompt;
package/src/cli/theme.js CHANGED
@@ -1,6 +1,4 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
2
  // src/cli/theme.ts
5
3
  function rgb(r, g, b) {
6
4
  return (value) => `\x1B[38;2;${r};${g};${b}m${value}\x1B[39m`;
package/src/cli/yargs.js CHANGED
@@ -1,12 +1,169 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
2
  // src/cli/yargs.ts
5
3
  import yargs from "yargs";
6
4
  import { hideBin } from "yargs/helpers";
7
5
  import pc from "picocolors";
8
- import { cols } from "@spader/dotllm/cli/layout";
9
- import { defaultTheme } from "@spader/dotllm/cli/theme";
6
+
7
+ // src/cli/theme.ts
8
+ function rgb(r, g, b) {
9
+ return (value) => `\x1B[38;2;${r};${g};${b}m${value}\x1B[39m`;
10
+ }
11
+ var gray = (value) => rgb(value, value, value);
12
+ var defaultTheme = {
13
+ primary: rgb(114, 161, 136),
14
+ link: rgb(114, 140, 212),
15
+ header: gray(128),
16
+ command: rgb(114, 161, 136),
17
+ arg: rgb(161, 212, 212),
18
+ option: rgb(212, 212, 161),
19
+ type: gray(128),
20
+ description: (value) => value,
21
+ dim: gray(128),
22
+ error: rgb(212, 114, 114),
23
+ success: rgb(114, 212, 136)
24
+ };
25
+
26
+ // src/cli/layout.ts
27
+ var ANSI_RE = /\x1b\[[0-9;]*m/g;
28
+ function clean(value) {
29
+ return value.replace(/[\t\n]/g, " ").replace(ANSI_RE, "");
30
+ }
31
+ function truncateMiddle(value, width) {
32
+ if (width <= 0)
33
+ return "";
34
+ if (value.length <= width)
35
+ return value;
36
+ if (width <= 3)
37
+ return value.slice(0, width);
38
+ const tail = Math.floor((width - 3) / 2);
39
+ const head = width - 3 - tail;
40
+ return `${value.slice(0, head)}...${value.slice(value.length - tail)}`;
41
+ }
42
+ function truncateStart(value, width) {
43
+ if (width <= 0)
44
+ return "";
45
+ if (value.length <= width)
46
+ return value;
47
+ if (width <= 3)
48
+ return "...".slice(0, width);
49
+ return `...${value.slice(value.length - (width - 3))}`;
50
+ }
51
+ function truncateEnd(value, width) {
52
+ if (width <= 0)
53
+ return "";
54
+ if (value.length <= width)
55
+ return value;
56
+ if (width <= 3)
57
+ return "...".slice(0, width);
58
+ return `${value.slice(0, width - 3)}...`;
59
+ }
60
+ function truncate(value, width, mode) {
61
+ if (mode === "start")
62
+ return truncateStart(value, width);
63
+ if (mode === "end")
64
+ return truncateEnd(value, width);
65
+ return truncateMiddle(value, width);
66
+ }
67
+ function fitWidths(natural, available, flex, noTruncate) {
68
+ const widths = [...natural];
69
+ const totalNatural = natural.reduce((sum, width) => sum + width, 0);
70
+ if (totalNatural <= available) {
71
+ return widths;
72
+ }
73
+ let fixedWidth = 0;
74
+ const dynamic = [];
75
+ for (let index = 0;index < widths.length; index++) {
76
+ if (noTruncate[index] || flex[index] === 0) {
77
+ fixedWidth += widths[index] ?? 0;
78
+ continue;
79
+ }
80
+ dynamic.push({ index, weight: flex[index] ?? 1 });
81
+ }
82
+ const remaining = Math.max(0, available - fixedWidth);
83
+ if (dynamic.length === 0) {
84
+ return widths;
85
+ }
86
+ const totalWeight = dynamic.reduce((sum, item) => sum + item.weight, 0);
87
+ let used = 0;
88
+ for (const item of dynamic) {
89
+ const share = Math.floor(remaining * item.weight / totalWeight);
90
+ const width = Math.max(1, share);
91
+ widths[item.index] = width;
92
+ used += width;
93
+ }
94
+ let extra = remaining - used;
95
+ let cursor = 0;
96
+ while (extra > 0) {
97
+ const item = dynamic[cursor % dynamic.length];
98
+ widths[item.index] = (widths[item.index] ?? 0) + 1;
99
+ extra--;
100
+ cursor++;
101
+ }
102
+ return widths;
103
+ }
104
+ function table(headers, columns, options = {}) {
105
+ if (headers.length === 0)
106
+ return;
107
+ const count = headers.length;
108
+ const gap = 2;
109
+ const rows = columns.reduce((max, column) => Math.max(max, column.length), 0);
110
+ const visibleRows = Math.min(rows, options.maxRows ?? rows);
111
+ const natural = [];
112
+ for (let col = 0;col < count; col++) {
113
+ const headerWidth = clean(headers[col] ?? "").length;
114
+ let width = headerWidth;
115
+ for (let row = 0;row < visibleRows; row++) {
116
+ const value = clean(columns[col]?.[row] ?? "");
117
+ width = Math.max(width, value.length);
118
+ }
119
+ natural[col] = width;
120
+ }
121
+ const maxWidth = options.maxWidth ?? (process.stdout.columns == null ? 120 : process.stdout.columns);
122
+ const available = Math.max(0, maxWidth - gap * (count - 1) - 1);
123
+ const widths = fitWidths(natural, available, options.flex ?? [], options.noTruncate ?? []);
124
+ const header = headers.map((title, col) => truncateMiddle(clean(title), widths[col] ?? 0).padEnd(widths[col] ?? 0)).join(" ");
125
+ process.stdout.write(`${defaultTheme.dim(header)}
126
+ `);
127
+ for (let row = 0;row < visibleRows; row++) {
128
+ const cells = [];
129
+ for (let col = 0;col < count; col++) {
130
+ const width = widths[col] ?? 0;
131
+ const source = clean(columns[col]?.[row] ?? "");
132
+ const mode = options.truncate?.[col] ?? "middle";
133
+ const value = options.noTruncate?.[col] ? source : truncate(source, width, mode);
134
+ const padded = width > 0 ? value.padEnd(width) : value;
135
+ const formatted = options.format?.[col]?.(padded, row, col) ?? padded;
136
+ cells.push(formatted);
137
+ }
138
+ process.stdout.write(`${cells.join(" ")}
139
+ `);
140
+ }
141
+ if (rows > visibleRows) {
142
+ process.stdout.write(`${defaultTheme.dim("(...truncated)")}
143
+ `);
144
+ }
145
+ }
146
+ function cols(rows, colorFns) {
147
+ if (rows.length === 0)
148
+ return;
149
+ const widths = rows[0].map((_, col) => {
150
+ let width = 0;
151
+ for (const row of rows) {
152
+ width = Math.max(width, clean(row[col] ?? "").length);
153
+ }
154
+ return width;
155
+ });
156
+ for (const row of rows) {
157
+ const line = row.map((value, col) => {
158
+ const padded = clean(value).padEnd(widths[col] ?? 0);
159
+ return colorFns?.[col]?.(padded) ?? padded;
160
+ }).join(" ");
161
+ process.stdout.write(`${line}
162
+ `);
163
+ }
164
+ }
165
+
166
+ // src/cli/yargs.ts
10
167
  function usage(def, path, t) {
11
168
  const parts = [];
12
169
  const last = path.length - 1;
@@ -173,6 +330,6 @@ function build(def) {
173
330
  return y;
174
331
  }
175
332
  export {
176
- help,
177
- build
333
+ build,
334
+ help
178
335
  };
package/src/core/add.js CHANGED
@@ -1,10 +1,161 @@
1
1
  // @bun
2
- var __require = import.meta.require;
2
+ var __esm = (fn, res, err) => () => {
3
+ if (fn)
4
+ try {
5
+ res = fn(fn = 0);
6
+ } catch (e) {
7
+ err = [e];
8
+ }
9
+ if (err)
10
+ throw err[0];
11
+ return res;
12
+ };
3
13
 
4
- // src/core/add.ts
14
+ // src/core/config.ts
5
15
  import fs from "fs";
16
+ import os from "os";
6
17
  import path from "path";
7
- import { Config } from "@spader/dotllm/core/config";
18
+ import { z } from "zod";
19
+ function readJson(filepath) {
20
+ if (!fs.existsSync(filepath))
21
+ return null;
22
+ const raw = fs.readFileSync(filepath, "utf-8");
23
+ try {
24
+ return JSON.parse(raw);
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ var LOCAL_DIR = ".llm", LOCAL_FILE, REF_DIR, RepoEntry, GlobalShape, LocalShape, Config;
30
+ var init_config = __esm(() => {
31
+ LOCAL_FILE = path.join(LOCAL_DIR, "dotllm.json");
32
+ REF_DIR = path.join(LOCAL_DIR, "reference");
33
+ RepoEntry = z.object({
34
+ kind: z.enum(["url", "file"]),
35
+ name: z.string(),
36
+ uri: z.string(),
37
+ description: z.string()
38
+ });
39
+ GlobalShape = z.object({
40
+ store: z.string().optional(),
41
+ repos: z.array(RepoEntry)
42
+ });
43
+ LocalShape = z.object({
44
+ refs: z.record(z.string(), RepoEntry)
45
+ });
46
+ ((Config) => {
47
+ function home() {
48
+ if (process.platform === "win32") {
49
+ const appData = process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
50
+ return path.join(appData, "dotllm");
51
+ }
52
+ return path.join(process.env.HOME ?? os.homedir(), ".local", "share", "dotllm");
53
+ }
54
+ Config.home = home;
55
+ function storeDir() {
56
+ const store = Global.read().store;
57
+ if (!store)
58
+ return path.join(home(), "store");
59
+ return expand(store);
60
+ }
61
+ Config.storeDir = storeDir;
62
+ function refDir() {
63
+ return REF_DIR;
64
+ }
65
+ Config.refDir = refDir;
66
+ let Global;
67
+ ((Global) => {
68
+ function read() {
69
+ const raw = readJson(path.join(home(), "dotllm.json"));
70
+ if (!raw)
71
+ return { repos: [] };
72
+ const result = GlobalShape.safeParse(raw);
73
+ if (!result.success)
74
+ return { repos: [] };
75
+ return result.data;
76
+ }
77
+ Global.read = read;
78
+ function write(config) {
79
+ const dir = home();
80
+ fs.mkdirSync(dir, { recursive: true });
81
+ fs.writeFileSync(path.join(dir, "dotllm.json"), JSON.stringify(config, null, 2) + `
82
+ `);
83
+ }
84
+ Global.write = write;
85
+ function find(config, name) {
86
+ const lower = name.toLowerCase();
87
+ return config.repos.find((r) => r.name.toLowerCase() === lower);
88
+ }
89
+ Global.find = find;
90
+ function add(config, entry) {
91
+ const lower = entry.name.toLowerCase();
92
+ const filtered = config.repos.filter((r) => r.name.toLowerCase() !== lower);
93
+ return { ...config, repos: [...filtered, entry] };
94
+ }
95
+ Global.add = add;
96
+ function remove(config, name) {
97
+ const lower = name.toLowerCase();
98
+ return { ...config, repos: config.repos.filter((r) => r.name.toLowerCase() !== lower) };
99
+ }
100
+ Global.remove = remove;
101
+ })(Global = Config.Global ||= {});
102
+ let Local;
103
+ ((Local) => {
104
+ function read() {
105
+ const raw = readJson(LOCAL_FILE);
106
+ if (!raw)
107
+ return { refs: {} };
108
+ const result = LocalShape.safeParse(raw);
109
+ if (!result.success)
110
+ return { refs: {} };
111
+ return result.data;
112
+ }
113
+ Local.read = read;
114
+ function write(config) {
115
+ fs.mkdirSync(LOCAL_DIR, { recursive: true });
116
+ fs.writeFileSync(LOCAL_FILE, JSON.stringify(config, null, 2) + `
117
+ `);
118
+ }
119
+ Local.write = write;
120
+ function find(config, name) {
121
+ const lower = name.toLowerCase();
122
+ for (const [key, value] of Object.entries(config.refs)) {
123
+ if (key.toLowerCase() === lower)
124
+ return value;
125
+ }
126
+ return;
127
+ }
128
+ Local.find = find;
129
+ function has(config, name) {
130
+ return find(config, name) !== undefined;
131
+ }
132
+ Local.has = has;
133
+ function add(config, repo) {
134
+ const lower = repo.name.toLowerCase();
135
+ const refs = Object.fromEntries(Object.entries(config.refs).filter(([key]) => key.toLowerCase() !== lower));
136
+ refs[repo.name] = repo;
137
+ return { refs };
138
+ }
139
+ Local.add = add;
140
+ function remove(config, name) {
141
+ const lower = name.toLowerCase();
142
+ const refs = Object.fromEntries(Object.entries(config.refs).filter(([key]) => key.toLowerCase() !== lower));
143
+ return { refs };
144
+ }
145
+ Local.remove = remove;
146
+ })(Local = Config.Local ||= {});
147
+ function expand(dir) {
148
+ const tilde = dir === "~" || dir.startsWith("~/") || dir.startsWith("~\\");
149
+ if (!tilde)
150
+ return path.resolve(home(), dir);
151
+ return path.join(os.homedir(), dir.slice(1));
152
+ }
153
+ })(Config ||= {});
154
+ });
155
+
156
+ // src/core/add.ts
157
+ import fs2 from "fs";
158
+ import path2 from "path";
8
159
  function isUrl(value) {
9
160
  return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("git@") || value.startsWith("ssh://");
10
161
  }
@@ -33,9 +184,9 @@ async function add(uri, name, description) {
33
184
  async function cloneUrl(url, name, description) {
34
185
  const resolved = name ?? nameFromUrl(url);
35
186
  const store = Config.storeDir();
36
- fs.mkdirSync(store, { recursive: true });
37
- const target = path.join(store, resolved);
38
- if (!fs.existsSync(target)) {
187
+ fs2.mkdirSync(store, { recursive: true });
188
+ const target = path2.join(store, resolved);
189
+ if (!fs2.existsSync(target)) {
39
190
  const proc = Bun.spawn(["git", "clone", "--depth=1", url, target], {
40
191
  stdout: "pipe",
41
192
  stderr: "pipe"
@@ -52,25 +203,30 @@ async function cloneUrl(url, name, description) {
52
203
  return { ok: true, entry, storePath: target };
53
204
  }
54
205
  function linkLocal(raw, name, description) {
55
- const resolved = path.resolve(raw);
56
- if (!fs.existsSync(resolved)) {
206
+ const resolved = path2.resolve(raw);
207
+ if (!fs2.existsSync(resolved)) {
57
208
  return { ok: false, error: `Path does not exist: ${resolved}` };
58
209
  }
59
- if (!fs.statSync(resolved).isDirectory()) {
210
+ if (!fs2.statSync(resolved).isDirectory()) {
60
211
  return { ok: false, error: `Not a directory: ${resolved}` };
61
212
  }
62
213
  const store = Config.storeDir();
63
- fs.mkdirSync(store, { recursive: true });
64
- const finalName = name ?? nameFromGitRemote(resolved) ?? path.basename(resolved);
65
- const target = path.join(store, finalName);
66
- if (!fs.existsSync(target)) {
67
- fs.symlinkSync(resolved, target, "dir");
214
+ fs2.mkdirSync(store, { recursive: true });
215
+ const finalName = name ?? nameFromGitRemote(resolved) ?? path2.basename(resolved);
216
+ const target = path2.join(store, finalName);
217
+ if (!fs2.existsSync(target)) {
218
+ fs2.symlinkSync(resolved, target, "dir");
68
219
  }
69
220
  const entry = { kind: "file", name: finalName, uri: resolved, description };
70
221
  const global = Config.Global.read();
71
222
  Config.Global.write(Config.Global.add(global, entry));
72
223
  return { ok: true, entry, storePath: target };
73
224
  }
225
+ var init_add = __esm(() => {
226
+ init_config();
227
+ });
228
+ init_add();
229
+
74
230
  export {
75
231
  add
76
232
  };