codealmanac 0.2.2 → 0.2.4
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/README.md +19 -6
- package/dist/agents-4Y7X24WW.js +25 -0
- package/dist/chunk-BF2J4XTC.js +766 -0
- package/dist/chunk-BF2J4XTC.js.map +1 -0
- package/dist/{chunk-B2AGSRXL.js → chunk-CW4HRLMS.js} +88 -7
- package/dist/chunk-CW4HRLMS.js.map +1 -0
- package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
- package/dist/chunk-H37GKBWI.js.map +1 -0
- package/dist/{chunk-MX2EW5MR.js → chunk-H6QKCB7M.js} +2 -2
- package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
- package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
- package/dist/chunk-P5WGG4FJ.js +359 -0
- package/dist/chunk-P5WGG4FJ.js.map +1 -0
- package/dist/{chunk-ZDJSJIB6.js → chunk-QRK3JLFX.js} +131 -15
- package/dist/chunk-QRK3JLFX.js.map +1 -0
- package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
- package/dist/chunk-TILAKDN6.js.map +1 -0
- package/dist/chunk-TT6ZP4GS.js +282 -0
- package/dist/chunk-TT6ZP4GS.js.map +1 -0
- package/dist/chunk-UU6FBRQO.js +187 -0
- package/dist/chunk-UU6FBRQO.js.map +1 -0
- package/dist/{cli-3XAVBTYG.js → cli-MYMZ66EN.js} +123 -32
- package/dist/cli-MYMZ66EN.js.map +1 -0
- package/dist/codealmanac.js +1 -1
- package/dist/config-ML2RCR7J.js +16 -0
- package/dist/{doctor-3BYSF3JD.js → doctor-W5KQQLAX.js} +6 -6
- package/dist/{register-commands-7QCIENRZ.js → register-commands-XTK2G2FB.js} +293 -393
- package/dist/register-commands-XTK2G2FB.js.map +1 -0
- package/dist/{uninstall-FDIOBAAR.js → uninstall-N7JY7ZV2.js} +5 -5
- package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
- package/guides/mini.md +1 -1
- package/guides/reference.md +68 -9
- package/package.json +1 -1
- package/dist/agents-A4II4YJC.js +0 -15
- package/dist/auth-S5DVUIUJ.js +0 -18
- package/dist/chunk-B2AGSRXL.js.map +0 -1
- package/dist/chunk-P3LDTCLB.js.map +0 -1
- package/dist/chunk-R3URPHGH.js +0 -194
- package/dist/chunk-R3URPHGH.js.map +0 -1
- package/dist/chunk-SSYMRT4I.js +0 -126
- package/dist/chunk-SSYMRT4I.js.map +0 -1
- package/dist/chunk-V3QOQSXI.js.map +0 -1
- package/dist/chunk-WRUSDYYE.js +0 -97
- package/dist/chunk-WRUSDYYE.js.map +0 -1
- package/dist/chunk-ZDJSJIB6.js.map +0 -1
- package/dist/cli-3XAVBTYG.js.map +0 -1
- package/dist/register-commands-7QCIENRZ.js.map +0 -1
- /package/dist/{agents-A4II4YJC.js.map → agents-4Y7X24WW.js.map} +0 -0
- /package/dist/{chunk-MX2EW5MR.js.map → chunk-H6QKCB7M.js.map} +0 -0
- /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
- /package/dist/{doctor-3BYSF3JD.js.map → doctor-W5KQQLAX.js.map} +0 -0
- /package/dist/{uninstall-FDIOBAAR.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
- /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
findNearestAlmanacDir,
|
|
4
|
+
getGlobalAlmanacDir,
|
|
5
|
+
getRepoAlmanacDir
|
|
6
|
+
} from "./chunk-7JUX4ADQ.js";
|
|
7
|
+
|
|
8
|
+
// src/update/config.ts
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
var AGENT_PROVIDER_IDS = ["claude", "codex", "cursor"];
|
|
13
|
+
function isAgentProviderId(value) {
|
|
14
|
+
return AGENT_PROVIDER_IDS.includes(value);
|
|
15
|
+
}
|
|
16
|
+
function defaultConfig() {
|
|
17
|
+
return {
|
|
18
|
+
update_notifier: true,
|
|
19
|
+
agent: {
|
|
20
|
+
default: "claude",
|
|
21
|
+
models: {
|
|
22
|
+
claude: null,
|
|
23
|
+
codex: null,
|
|
24
|
+
cursor: null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function getConfigPath() {
|
|
30
|
+
return join(getGlobalAlmanacDir(), "config.toml");
|
|
31
|
+
}
|
|
32
|
+
function getLegacyConfigPath() {
|
|
33
|
+
return join(getGlobalAlmanacDir(), "config.json");
|
|
34
|
+
}
|
|
35
|
+
function getProjectConfigPath(cwd) {
|
|
36
|
+
const repoRoot = findNearestAlmanacDir(cwd);
|
|
37
|
+
return repoRoot === null ? null : join(getRepoAlmanacDir(repoRoot), "config.toml");
|
|
38
|
+
}
|
|
39
|
+
async function readConfig(input) {
|
|
40
|
+
return (await readConfigWithOrigins(input)).config;
|
|
41
|
+
}
|
|
42
|
+
async function readConfigWithOrigins(input) {
|
|
43
|
+
const opts = normalizeReadOptions(input);
|
|
44
|
+
if (opts.path !== void 0) {
|
|
45
|
+
const raw = await readRawConfigObject(opts.path);
|
|
46
|
+
return {
|
|
47
|
+
config: normalizeRawConfig(raw),
|
|
48
|
+
origins: originsFromRaw(raw, "user"),
|
|
49
|
+
raw
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const file = getConfigPath();
|
|
53
|
+
await migrateLegacyConfigIfNeeded(file);
|
|
54
|
+
const userRaw = await readRawConfigObject(file);
|
|
55
|
+
const mergedRaw = cloneJsonObject(userRaw);
|
|
56
|
+
const origins = originsFromRaw(userRaw, "user");
|
|
57
|
+
const projectPath = opts.cwd !== void 0 ? getProjectConfigPath(opts.cwd) : null;
|
|
58
|
+
if (projectPath !== null) {
|
|
59
|
+
const projectRaw = await readRawConfigObject(projectPath);
|
|
60
|
+
applyProjectConfig(mergedRaw, projectRaw);
|
|
61
|
+
Object.assign(origins, originsFromRaw(projectRaw, "project", true));
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
config: normalizeRawConfig(mergedRaw),
|
|
65
|
+
origins,
|
|
66
|
+
raw: mergedRaw
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function normalizeReadOptions(input) {
|
|
70
|
+
return typeof input === "string" ? { path: input } : input ?? {};
|
|
71
|
+
}
|
|
72
|
+
async function migrateLegacyConfigIfNeeded(file) {
|
|
73
|
+
if (existsSync(file)) return;
|
|
74
|
+
const legacy = getLegacyConfigPath();
|
|
75
|
+
if (!existsSync(legacy)) return;
|
|
76
|
+
const raw = await readRawConfigObject(legacy);
|
|
77
|
+
if (Object.keys(raw).length === 0) return;
|
|
78
|
+
await writeConfig(normalizeRawConfig(raw), file);
|
|
79
|
+
}
|
|
80
|
+
function normalizeRawConfig(raw) {
|
|
81
|
+
const defaults = defaultConfig();
|
|
82
|
+
const rawAgent = raw.agent !== void 0 && raw.agent !== null && typeof raw.agent === "object" && !Array.isArray(raw.agent) ? raw.agent : {};
|
|
83
|
+
const rawDefault = typeof rawAgent.default === "string" && isAgentProviderId(rawAgent.default) ? rawAgent.default : defaults.agent.default;
|
|
84
|
+
const rawModels = rawAgent.models !== void 0 && rawAgent.models !== null && typeof rawAgent.models === "object" && !Array.isArray(rawAgent.models) ? rawAgent.models : {};
|
|
85
|
+
const models = {
|
|
86
|
+
...defaults.agent.models
|
|
87
|
+
};
|
|
88
|
+
for (const id of AGENT_PROVIDER_IDS) {
|
|
89
|
+
const value = rawModels[id];
|
|
90
|
+
if (typeof value === "string" && value.length > 0) {
|
|
91
|
+
models[id] = value === "default" || value === "null" ? null : value;
|
|
92
|
+
} else if (value === null) {
|
|
93
|
+
models[id] = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
update_notifier: typeof raw.update_notifier === "boolean" ? raw.update_notifier : defaults.update_notifier,
|
|
98
|
+
agent: {
|
|
99
|
+
default: rawDefault,
|
|
100
|
+
models
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function applyProjectConfig(target, projectRaw) {
|
|
105
|
+
const projectAgent = projectRaw.agent !== null && typeof projectRaw.agent === "object" && !Array.isArray(projectRaw.agent) ? projectRaw.agent : {};
|
|
106
|
+
if (Object.keys(projectAgent).length === 0) return;
|
|
107
|
+
const targetAgent = target.agent !== null && typeof target.agent === "object" && !Array.isArray(target.agent) ? target.agent : {};
|
|
108
|
+
target.agent = targetAgent;
|
|
109
|
+
if (typeof projectAgent.default === "string") {
|
|
110
|
+
targetAgent.default = projectAgent.default;
|
|
111
|
+
}
|
|
112
|
+
const projectModels = projectAgent.models !== null && typeof projectAgent.models === "object" && !Array.isArray(projectAgent.models) ? projectAgent.models : {};
|
|
113
|
+
if (Object.keys(projectModels).length === 0) return;
|
|
114
|
+
const targetModels = targetAgent.models !== null && typeof targetAgent.models === "object" && !Array.isArray(targetAgent.models) ? targetAgent.models : {};
|
|
115
|
+
targetAgent.models = targetModels;
|
|
116
|
+
for (const id of AGENT_PROVIDER_IDS) {
|
|
117
|
+
if (Object.prototype.hasOwnProperty.call(projectModels, id)) {
|
|
118
|
+
targetModels[id] = projectModels[id];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function originsFromRaw(raw, origin, agentOnly = false) {
|
|
123
|
+
const origins = {};
|
|
124
|
+
if (!agentOnly && Object.prototype.hasOwnProperty.call(raw, "update_notifier")) {
|
|
125
|
+
origins.update_notifier = origin;
|
|
126
|
+
}
|
|
127
|
+
const agent = raw.agent !== null && typeof raw.agent === "object" && !Array.isArray(raw.agent) ? raw.agent : {};
|
|
128
|
+
if (Object.prototype.hasOwnProperty.call(agent, "default")) {
|
|
129
|
+
origins["agent.default"] = origin;
|
|
130
|
+
}
|
|
131
|
+
const models = agent.models !== null && typeof agent.models === "object" && !Array.isArray(agent.models) ? agent.models : {};
|
|
132
|
+
for (const id of AGENT_PROVIDER_IDS) {
|
|
133
|
+
if (Object.prototype.hasOwnProperty.call(models, id)) {
|
|
134
|
+
origins[`agent.models.${id}`] = origin;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return origins;
|
|
138
|
+
}
|
|
139
|
+
async function readSingleConfig(file) {
|
|
140
|
+
let raw;
|
|
141
|
+
try {
|
|
142
|
+
raw = await readFile(file, "utf8");
|
|
143
|
+
} catch {
|
|
144
|
+
return defaultConfig();
|
|
145
|
+
}
|
|
146
|
+
const trimmed = raw.trim();
|
|
147
|
+
if (trimmed.length === 0) return defaultConfig();
|
|
148
|
+
try {
|
|
149
|
+
return normalizeRawConfig(parseConfigText(trimmed, file));
|
|
150
|
+
} catch {
|
|
151
|
+
return defaultConfig();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function writeConfig(config, path) {
|
|
155
|
+
const file = path ?? getConfigPath();
|
|
156
|
+
await mkdir(dirname(file), { recursive: true });
|
|
157
|
+
const current = await readSingleConfig(file);
|
|
158
|
+
const existingRaw = await readRawConfigObject(file);
|
|
159
|
+
const stored = toStoredConfigPatch(config, current, existingRaw);
|
|
160
|
+
const body = serializeConfig(stored, file);
|
|
161
|
+
const tmp = `${file}.tmp`;
|
|
162
|
+
await writeFile(tmp, body, "utf8");
|
|
163
|
+
await rename(tmp, file);
|
|
164
|
+
}
|
|
165
|
+
function normalizeConfig(config) {
|
|
166
|
+
const defaults = defaultConfig();
|
|
167
|
+
return {
|
|
168
|
+
update_notifier: typeof config.update_notifier === "boolean" ? config.update_notifier : defaults.update_notifier,
|
|
169
|
+
agent: {
|
|
170
|
+
default: config.agent !== void 0 && isAgentProviderId(config.agent.default) ? config.agent.default : defaults.agent.default,
|
|
171
|
+
models: {
|
|
172
|
+
...defaults.agent.models,
|
|
173
|
+
...config.agent?.models ?? {}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function readRawConfigObject(path) {
|
|
179
|
+
try {
|
|
180
|
+
return parseConfigText(await readFile(path, "utf8"), path);
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
function parseConfigText(raw, path = "config.toml") {
|
|
186
|
+
const trimmed = raw.trim();
|
|
187
|
+
if (trimmed.length === 0) return {};
|
|
188
|
+
if (path.endsWith(".json") || trimmed.startsWith("{")) {
|
|
189
|
+
const parsed = JSON.parse(trimmed);
|
|
190
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
191
|
+
}
|
|
192
|
+
return parseTomlConfig(trimmed);
|
|
193
|
+
}
|
|
194
|
+
function serializeConfig(raw, path = "config.toml") {
|
|
195
|
+
return path.endsWith(".json") ? `${JSON.stringify(raw, null, 2)}
|
|
196
|
+
` : serializeTomlConfig(raw);
|
|
197
|
+
}
|
|
198
|
+
function parseTomlConfig(raw) {
|
|
199
|
+
const result = {};
|
|
200
|
+
let section = [];
|
|
201
|
+
for (const original of raw.split(/\r?\n/)) {
|
|
202
|
+
const line = stripTomlComment(original).trim();
|
|
203
|
+
if (line.length === 0) continue;
|
|
204
|
+
const sectionMatch = line.match(/^\[([A-Za-z0-9_.-]+)\]$/);
|
|
205
|
+
if (sectionMatch !== null) {
|
|
206
|
+
section = sectionMatch[1].split(".");
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const eq = line.indexOf("=");
|
|
210
|
+
if (eq === -1) continue;
|
|
211
|
+
const key = line.slice(0, eq).trim();
|
|
212
|
+
const value = parseTomlValue(line.slice(eq + 1).trim());
|
|
213
|
+
setObjectPath(result, [...section, key], value);
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
function serializeTomlConfig(raw) {
|
|
218
|
+
const lines = [];
|
|
219
|
+
if (typeof raw.update_notifier === "boolean") {
|
|
220
|
+
lines.push(`update_notifier = ${raw.update_notifier ? "true" : "false"}`);
|
|
221
|
+
}
|
|
222
|
+
const agent = raw.agent !== null && typeof raw.agent === "object" && !Array.isArray(raw.agent) ? raw.agent : {};
|
|
223
|
+
if (typeof agent.default === "string") {
|
|
224
|
+
if (lines.length > 0) lines.push("");
|
|
225
|
+
lines.push("[agent]");
|
|
226
|
+
lines.push(`default = ${tomlString(agent.default)}`);
|
|
227
|
+
}
|
|
228
|
+
const models = agent.models !== null && typeof agent.models === "object" && !Array.isArray(agent.models) ? agent.models : {};
|
|
229
|
+
const modelLines = [];
|
|
230
|
+
for (const id of AGENT_PROVIDER_IDS) {
|
|
231
|
+
if (!Object.prototype.hasOwnProperty.call(models, id)) continue;
|
|
232
|
+
const value = models[id] === null ? "default" : models[id];
|
|
233
|
+
if (typeof value === "string" && value.length > 0) {
|
|
234
|
+
modelLines.push(`${id} = ${tomlString(value)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (modelLines.length > 0) {
|
|
238
|
+
if (lines.length > 0) lines.push("");
|
|
239
|
+
lines.push("[agent.models]", ...modelLines);
|
|
240
|
+
}
|
|
241
|
+
return `${lines.join("\n")}
|
|
242
|
+
`;
|
|
243
|
+
}
|
|
244
|
+
function stripTomlComment(line) {
|
|
245
|
+
let inString = false;
|
|
246
|
+
let escaped = false;
|
|
247
|
+
for (let i = 0; i < line.length; i++) {
|
|
248
|
+
const ch = line[i];
|
|
249
|
+
if (escaped) {
|
|
250
|
+
escaped = false;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (ch === "\\") {
|
|
254
|
+
escaped = true;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (ch === '"') inString = !inString;
|
|
258
|
+
if (ch === "#" && !inString) return line.slice(0, i);
|
|
259
|
+
}
|
|
260
|
+
return line;
|
|
261
|
+
}
|
|
262
|
+
function parseTomlValue(raw) {
|
|
263
|
+
if (raw === "true") return true;
|
|
264
|
+
if (raw === "false") return false;
|
|
265
|
+
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
266
|
+
return JSON.parse(raw);
|
|
267
|
+
}
|
|
268
|
+
return raw;
|
|
269
|
+
}
|
|
270
|
+
function tomlString(value) {
|
|
271
|
+
return JSON.stringify(value);
|
|
272
|
+
}
|
|
273
|
+
function setObjectPath(raw, path, value) {
|
|
274
|
+
let cursor = raw;
|
|
275
|
+
for (const part of path.slice(0, -1)) {
|
|
276
|
+
const next = cursor[part];
|
|
277
|
+
if (next === null || typeof next !== "object" || Array.isArray(next)) {
|
|
278
|
+
cursor[part] = {};
|
|
279
|
+
}
|
|
280
|
+
cursor = cursor[part];
|
|
281
|
+
}
|
|
282
|
+
const leaf = path[path.length - 1];
|
|
283
|
+
if (leaf !== void 0) cursor[leaf] = value;
|
|
284
|
+
}
|
|
285
|
+
function toStoredConfigPatch(config, current, raw) {
|
|
286
|
+
const normalized = normalizeConfig(config);
|
|
287
|
+
const defaults = defaultConfig();
|
|
288
|
+
const stored = cloneJsonObject(raw);
|
|
289
|
+
if (config.update_notifier !== void 0 && normalized.update_notifier !== current.update_notifier) {
|
|
290
|
+
setStoredValue(
|
|
291
|
+
stored,
|
|
292
|
+
["update_notifier"],
|
|
293
|
+
normalized.update_notifier,
|
|
294
|
+
defaults.update_notifier
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (config.agent !== void 0) {
|
|
298
|
+
if (config.agent.default !== void 0 && normalized.agent.default !== current.agent.default) {
|
|
299
|
+
setStoredValue(
|
|
300
|
+
stored,
|
|
301
|
+
["agent", "default"],
|
|
302
|
+
normalized.agent.default,
|
|
303
|
+
defaults.agent.default
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const inputModels = config.agent.models ?? {};
|
|
307
|
+
for (const id of AGENT_PROVIDER_IDS) {
|
|
308
|
+
if (!Object.prototype.hasOwnProperty.call(inputModels, id)) continue;
|
|
309
|
+
const value = normalized.agent.models[id] ?? null;
|
|
310
|
+
const currentValue = current.agent.models[id] ?? null;
|
|
311
|
+
const defaultValue = defaults.agent.models[id] ?? null;
|
|
312
|
+
if (value !== currentValue) {
|
|
313
|
+
setStoredValue(stored, ["agent", "models", id], value, defaultValue);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
pruneEmptyObjects(stored);
|
|
318
|
+
return stored;
|
|
319
|
+
}
|
|
320
|
+
function setStoredValue(raw, path, value, defaultValue) {
|
|
321
|
+
let cursor = raw;
|
|
322
|
+
for (const part of path.slice(0, -1)) {
|
|
323
|
+
const next = cursor[part];
|
|
324
|
+
if (next === null || typeof next !== "object" || Array.isArray(next)) {
|
|
325
|
+
cursor[part] = {};
|
|
326
|
+
}
|
|
327
|
+
cursor = cursor[part];
|
|
328
|
+
}
|
|
329
|
+
const leaf = path[path.length - 1];
|
|
330
|
+
if (leaf === void 0) return;
|
|
331
|
+
cursor[leaf] = value;
|
|
332
|
+
if (value !== defaultValue) return;
|
|
333
|
+
}
|
|
334
|
+
function cloneJsonObject(raw) {
|
|
335
|
+
return JSON.parse(JSON.stringify(raw));
|
|
336
|
+
}
|
|
337
|
+
function pruneEmptyObjects(raw) {
|
|
338
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
339
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
pruneEmptyObjects(value);
|
|
343
|
+
if (Object.keys(value).length === 0) delete raw[key];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export {
|
|
348
|
+
AGENT_PROVIDER_IDS,
|
|
349
|
+
isAgentProviderId,
|
|
350
|
+
getConfigPath,
|
|
351
|
+
getLegacyConfigPath,
|
|
352
|
+
getProjectConfigPath,
|
|
353
|
+
readConfig,
|
|
354
|
+
readConfigWithOrigins,
|
|
355
|
+
writeConfig,
|
|
356
|
+
parseConfigText,
|
|
357
|
+
serializeConfig
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=chunk-P5WGG4FJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/update/config.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport {\n findNearestAlmanacDir,\n getGlobalAlmanacDir,\n getRepoAlmanacDir,\n} from \"../paths.js\";\n\nexport const AGENT_PROVIDER_IDS = [\"claude\", \"codex\", \"cursor\"] as const;\nexport type AgentProviderId = (typeof AGENT_PROVIDER_IDS)[number];\n\nexport function isAgentProviderId(value: string): value is AgentProviderId {\n return (AGENT_PROVIDER_IDS as readonly string[]).includes(value);\n}\n\nexport interface AgentConfig {\n /** Default provider for bootstrap/capture. Default: \"claude\". */\n default: AgentProviderId;\n /** Optional per-provider model override. `null` means provider default. */\n models: Partial<Record<AgentProviderId, string | null>>;\n}\n\n/**\n * `~/.almanac/config.toml` — global, cross-wiki configuration. Legacy\n * `config.json` is read and migrated forward on first normal access.\n *\n * Missing or malformed → defaults. Same tolerance as `UpdateState`:\n * the CLI must not be able to fail because this file drifted.\n */\nexport interface GlobalConfig {\n /** When `false`, suppress the pre-command update-nag banner. Default: true. */\n update_notifier: boolean;\n /** Agent-provider settings for bootstrap/capture. */\n agent: AgentConfig;\n}\n\nexport function defaultConfig(): GlobalConfig {\n return {\n update_notifier: true,\n agent: {\n default: \"claude\",\n models: {\n claude: null,\n codex: null,\n cursor: null,\n },\n },\n };\n}\n\nexport function getConfigPath(): string {\n return join(getGlobalAlmanacDir(), \"config.toml\");\n}\n\nexport function getLegacyConfigPath(): string {\n return join(getGlobalAlmanacDir(), \"config.json\");\n}\n\nexport function getProjectConfigPath(cwd: string): string | null {\n const repoRoot = findNearestAlmanacDir(cwd);\n return repoRoot === null ? null : join(getRepoAlmanacDir(repoRoot), \"config.toml\");\n}\n\nexport type ConfigOrigin = \"default\" | \"user\" | \"project\";\n\nexport interface ConfigReadOptions {\n path?: string;\n cwd?: string;\n}\n\nexport interface ConfigReadResult {\n config: GlobalConfig;\n origins: Record<string, ConfigOrigin>;\n raw: Record<string, unknown>;\n}\n\nexport async function readConfig(\n input?: string | ConfigReadOptions,\n): Promise<GlobalConfig> {\n return (await readConfigWithOrigins(input)).config;\n}\n\nexport async function readConfigWithOrigins(\n input?: string | ConfigReadOptions,\n): Promise<ConfigReadResult> {\n const opts = normalizeReadOptions(input);\n if (opts.path !== undefined) {\n const raw = await readRawConfigObject(opts.path);\n return {\n config: normalizeRawConfig(raw),\n origins: originsFromRaw(raw, \"user\"),\n raw,\n };\n }\n\n const file = getConfigPath();\n await migrateLegacyConfigIfNeeded(file);\n const userRaw = await readRawConfigObject(file);\n const mergedRaw = cloneJsonObject(userRaw);\n const origins = originsFromRaw(userRaw, \"user\");\n const projectPath = opts.cwd !== undefined ? getProjectConfigPath(opts.cwd) : null;\n if (projectPath !== null) {\n const projectRaw = await readRawConfigObject(projectPath);\n applyProjectConfig(mergedRaw, projectRaw);\n Object.assign(origins, originsFromRaw(projectRaw, \"project\", true));\n }\n return {\n config: normalizeRawConfig(mergedRaw),\n origins,\n raw: mergedRaw,\n };\n}\n\nfunction normalizeReadOptions(\n input?: string | ConfigReadOptions,\n): ConfigReadOptions {\n return typeof input === \"string\" ? { path: input } : input ?? {};\n}\n\nasync function migrateLegacyConfigIfNeeded(file: string): Promise<void> {\n if (existsSync(file)) return;\n const legacy = getLegacyConfigPath();\n if (!existsSync(legacy)) return;\n const raw = await readRawConfigObject(legacy);\n if (Object.keys(raw).length === 0) return;\n await writeConfig(normalizeRawConfig(raw), file);\n}\n\nfunction normalizeRawConfig(raw: Record<string, unknown>): GlobalConfig {\n const defaults = defaultConfig();\n const rawAgent =\n raw.agent !== undefined &&\n raw.agent !== null &&\n typeof raw.agent === \"object\" &&\n !Array.isArray(raw.agent)\n ? (raw.agent as Partial<AgentConfig>)\n : {};\n const rawDefault =\n typeof rawAgent.default === \"string\" &&\n isAgentProviderId(rawAgent.default)\n ? rawAgent.default\n : defaults.agent.default;\n const rawModels =\n rawAgent.models !== undefined &&\n rawAgent.models !== null &&\n typeof rawAgent.models === \"object\" &&\n !Array.isArray(rawAgent.models)\n ? (rawAgent.models as Record<string, unknown>)\n : {};\n const models: Partial<Record<AgentProviderId, string | null>> = {\n ...defaults.agent.models,\n };\n for (const id of AGENT_PROVIDER_IDS) {\n const value = rawModels[id];\n if (typeof value === \"string\" && value.length > 0) {\n models[id] = value === \"default\" || value === \"null\" ? null : value;\n } else if (value === null) {\n models[id] = null;\n }\n }\n return {\n update_notifier:\n typeof raw.update_notifier === \"boolean\"\n ? raw.update_notifier\n : defaults.update_notifier,\n agent: {\n default: rawDefault,\n models,\n },\n };\n}\n\nfunction applyProjectConfig(\n target: Record<string, unknown>,\n projectRaw: Record<string, unknown>,\n): void {\n const projectAgent =\n projectRaw.agent !== null &&\n typeof projectRaw.agent === \"object\" &&\n !Array.isArray(projectRaw.agent)\n ? projectRaw.agent as Record<string, unknown>\n : {};\n if (Object.keys(projectAgent).length === 0) return;\n const targetAgent =\n target.agent !== null &&\n typeof target.agent === \"object\" &&\n !Array.isArray(target.agent)\n ? target.agent as Record<string, unknown>\n : {};\n target.agent = targetAgent;\n if (typeof projectAgent.default === \"string\") {\n targetAgent.default = projectAgent.default;\n }\n const projectModels =\n projectAgent.models !== null &&\n typeof projectAgent.models === \"object\" &&\n !Array.isArray(projectAgent.models)\n ? projectAgent.models as Record<string, unknown>\n : {};\n if (Object.keys(projectModels).length === 0) return;\n const targetModels =\n targetAgent.models !== null &&\n typeof targetAgent.models === \"object\" &&\n !Array.isArray(targetAgent.models)\n ? targetAgent.models as Record<string, unknown>\n : {};\n targetAgent.models = targetModels;\n for (const id of AGENT_PROVIDER_IDS) {\n if (Object.prototype.hasOwnProperty.call(projectModels, id)) {\n targetModels[id] = projectModels[id];\n }\n }\n}\n\nfunction originsFromRaw(\n raw: Record<string, unknown>,\n origin: ConfigOrigin,\n agentOnly = false,\n): Record<string, ConfigOrigin> {\n const origins: Record<string, ConfigOrigin> = {};\n if (!agentOnly && Object.prototype.hasOwnProperty.call(raw, \"update_notifier\")) {\n origins.update_notifier = origin;\n }\n const agent =\n raw.agent !== null &&\n typeof raw.agent === \"object\" &&\n !Array.isArray(raw.agent)\n ? raw.agent as Record<string, unknown>\n : {};\n if (Object.prototype.hasOwnProperty.call(agent, \"default\")) {\n origins[\"agent.default\"] = origin;\n }\n const models =\n agent.models !== null &&\n typeof agent.models === \"object\" &&\n !Array.isArray(agent.models)\n ? agent.models as Record<string, unknown>\n : {};\n for (const id of AGENT_PROVIDER_IDS) {\n if (Object.prototype.hasOwnProperty.call(models, id)) {\n origins[`agent.models.${id}`] = origin;\n }\n }\n return origins;\n}\n\nasync function readSingleConfig(file: string): Promise<GlobalConfig> {\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch {\n return defaultConfig();\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) return defaultConfig();\n try {\n return normalizeRawConfig(parseConfigText(trimmed, file));\n } catch {\n return defaultConfig();\n }\n}\n\nexport async function writeConfig(\n config: GlobalConfig | Partial<GlobalConfig>,\n path?: string,\n): Promise<void> {\n const file = path ?? getConfigPath();\n await mkdir(dirname(file), { recursive: true });\n const current = await readSingleConfig(file);\n const existingRaw = await readRawConfigObject(file);\n const stored = toStoredConfigPatch(config, current, existingRaw);\n const body = serializeConfig(stored, file);\n const tmp = `${file}.tmp`;\n await writeFile(tmp, body, \"utf8\");\n await rename(tmp, file);\n}\n\nfunction normalizeConfig(config: GlobalConfig | Partial<GlobalConfig>): GlobalConfig {\n const defaults = defaultConfig();\n return {\n update_notifier:\n typeof config.update_notifier === \"boolean\"\n ? config.update_notifier\n : defaults.update_notifier,\n agent: {\n default:\n config.agent !== undefined && isAgentProviderId(config.agent.default)\n ? config.agent.default\n : defaults.agent.default,\n models: {\n ...defaults.agent.models,\n ...(config.agent?.models ?? {}),\n },\n },\n };\n}\n\nasync function readRawConfigObject(\n path: string,\n): Promise<Record<string, unknown>> {\n try {\n return parseConfigText(await readFile(path, \"utf8\"), path);\n } catch {\n // Fall through to empty.\n }\n return {};\n}\n\nexport function parseConfigText(\n raw: string,\n path = \"config.toml\",\n): Record<string, unknown> {\n const trimmed = raw.trim();\n if (trimmed.length === 0) return {};\n if (path.endsWith(\".json\") || trimmed.startsWith(\"{\")) {\n const parsed = JSON.parse(trimmed) as unknown;\n return parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {};\n }\n return parseTomlConfig(trimmed);\n}\n\nexport function serializeConfig(\n raw: Record<string, unknown>,\n path = \"config.toml\",\n): string {\n return path.endsWith(\".json\")\n ? `${JSON.stringify(raw, null, 2)}\\n`\n : serializeTomlConfig(raw);\n}\n\nfunction parseTomlConfig(raw: string): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n let section: string[] = [];\n for (const original of raw.split(/\\r?\\n/)) {\n const line = stripTomlComment(original).trim();\n if (line.length === 0) continue;\n const sectionMatch = line.match(/^\\[([A-Za-z0-9_.-]+)\\]$/);\n if (sectionMatch !== null) {\n section = sectionMatch[1]!.split(\".\");\n continue;\n }\n const eq = line.indexOf(\"=\");\n if (eq === -1) continue;\n const key = line.slice(0, eq).trim();\n const value = parseTomlValue(line.slice(eq + 1).trim());\n setObjectPath(result, [...section, key], value);\n }\n return result;\n}\n\nfunction serializeTomlConfig(raw: Record<string, unknown>): string {\n const lines: string[] = [];\n if (typeof raw.update_notifier === \"boolean\") {\n lines.push(`update_notifier = ${raw.update_notifier ? \"true\" : \"false\"}`);\n }\n const agent =\n raw.agent !== null &&\n typeof raw.agent === \"object\" &&\n !Array.isArray(raw.agent)\n ? raw.agent as Record<string, unknown>\n : {};\n if (typeof agent.default === \"string\") {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"[agent]\");\n lines.push(`default = ${tomlString(agent.default)}`);\n }\n const models =\n agent.models !== null &&\n typeof agent.models === \"object\" &&\n !Array.isArray(agent.models)\n ? agent.models as Record<string, unknown>\n : {};\n const modelLines: string[] = [];\n for (const id of AGENT_PROVIDER_IDS) {\n if (!Object.prototype.hasOwnProperty.call(models, id)) continue;\n const value = models[id] === null ? \"default\" : models[id];\n if (typeof value === \"string\" && value.length > 0) {\n modelLines.push(`${id} = ${tomlString(value)}`);\n }\n }\n if (modelLines.length > 0) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"[agent.models]\", ...modelLines);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction stripTomlComment(line: string): string {\n let inString = false;\n let escaped = false;\n for (let i = 0; i < line.length; i++) {\n const ch = line[i];\n if (escaped) {\n escaped = false;\n continue;\n }\n if (ch === \"\\\\\") {\n escaped = true;\n continue;\n }\n if (ch === \"\\\"\") inString = !inString;\n if (ch === \"#\" && !inString) return line.slice(0, i);\n }\n return line;\n}\n\nfunction parseTomlValue(raw: string): string | boolean {\n if (raw === \"true\") return true;\n if (raw === \"false\") return false;\n if (raw.startsWith(\"\\\"\") && raw.endsWith(\"\\\"\")) {\n return JSON.parse(raw) as string;\n }\n return raw;\n}\n\nfunction tomlString(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction setObjectPath(\n raw: Record<string, unknown>,\n path: string[],\n value: string | boolean,\n): void {\n let cursor = raw;\n for (const part of path.slice(0, -1)) {\n const next = cursor[part];\n if (next === null || typeof next !== \"object\" || Array.isArray(next)) {\n cursor[part] = {};\n }\n cursor = cursor[part] as Record<string, unknown>;\n }\n const leaf = path[path.length - 1];\n if (leaf !== undefined) cursor[leaf] = value;\n}\n\nfunction toStoredConfigPatch(\n config: GlobalConfig | Partial<GlobalConfig>,\n current: GlobalConfig,\n raw: Record<string, unknown>,\n): Record<string, unknown> {\n const normalized = normalizeConfig(config);\n const defaults = defaultConfig();\n const stored = cloneJsonObject(raw);\n\n if (\n config.update_notifier !== undefined &&\n normalized.update_notifier !== current.update_notifier\n ) {\n setStoredValue(\n stored,\n [\"update_notifier\"],\n normalized.update_notifier,\n defaults.update_notifier,\n );\n }\n\n if (config.agent !== undefined) {\n if (\n config.agent.default !== undefined &&\n normalized.agent.default !== current.agent.default\n ) {\n setStoredValue(\n stored,\n [\"agent\", \"default\"],\n normalized.agent.default,\n defaults.agent.default,\n );\n }\n\n const inputModels = config.agent.models ?? {};\n for (const id of AGENT_PROVIDER_IDS) {\n if (!Object.prototype.hasOwnProperty.call(inputModels, id)) continue;\n const value = normalized.agent.models[id] ?? null;\n const currentValue = current.agent.models[id] ?? null;\n const defaultValue = defaults.agent.models[id] ?? null;\n if (value !== currentValue) {\n setStoredValue(stored, [\"agent\", \"models\", id], value, defaultValue);\n }\n }\n }\n pruneEmptyObjects(stored);\n return stored;\n}\n\nfunction setStoredValue(\n raw: Record<string, unknown>,\n path: string[],\n value: string | boolean | null,\n defaultValue: string | boolean | null,\n): void {\n let cursor = raw;\n for (const part of path.slice(0, -1)) {\n const next = cursor[part];\n if (next === null || typeof next !== \"object\" || Array.isArray(next)) {\n cursor[part] = {};\n }\n cursor = cursor[part] as Record<string, unknown>;\n }\n const leaf = path[path.length - 1];\n if (leaf === undefined) return;\n cursor[leaf] = value;\n if (value !== defaultValue) return;\n // Keep explicit defaults only when the caller changed the value to the\n // default. Unchanged explicit defaults are preserved by cloning `raw`.\n}\n\nfunction cloneJsonObject(raw: Record<string, unknown>): Record<string, unknown> {\n return JSON.parse(JSON.stringify(raw)) as Record<string, unknown>;\n}\n\nfunction pruneEmptyObjects(raw: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(raw)) {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n continue;\n }\n pruneEmptyObjects(value as Record<string, unknown>);\n if (Object.keys(value).length === 0) delete raw[key];\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,OAAO,UAAU,QAAQ,iBAAiB;AACnD,SAAS,SAAS,YAAY;AAQvB,IAAM,qBAAqB,CAAC,UAAU,SAAS,QAAQ;AAGvD,SAAS,kBAAkB,OAAyC;AACzE,SAAQ,mBAAyC,SAAS,KAAK;AACjE;AAuBO,SAAS,gBAA8B;AAC5C,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,OAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,gBAAwB;AACtC,SAAO,KAAK,oBAAoB,GAAG,aAAa;AAClD;AAEO,SAAS,sBAA8B;AAC5C,SAAO,KAAK,oBAAoB,GAAG,aAAa;AAClD;AAEO,SAAS,qBAAqB,KAA4B;AAC/D,QAAM,WAAW,sBAAsB,GAAG;AAC1C,SAAO,aAAa,OAAO,OAAO,KAAK,kBAAkB,QAAQ,GAAG,aAAa;AACnF;AAeA,eAAsB,WACpB,OACuB;AACvB,UAAQ,MAAM,sBAAsB,KAAK,GAAG;AAC9C;AAEA,eAAsB,sBACpB,OAC2B;AAC3B,QAAM,OAAO,qBAAqB,KAAK;AACvC,MAAI,KAAK,SAAS,QAAW;AAC3B,UAAM,MAAM,MAAM,oBAAoB,KAAK,IAAI;AAC/C,WAAO;AAAA,MACL,QAAQ,mBAAmB,GAAG;AAAA,MAC9B,SAAS,eAAe,KAAK,MAAM;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO,cAAc;AAC3B,QAAM,4BAA4B,IAAI;AACtC,QAAM,UAAU,MAAM,oBAAoB,IAAI;AAC9C,QAAM,YAAY,gBAAgB,OAAO;AACzC,QAAM,UAAU,eAAe,SAAS,MAAM;AAC9C,QAAM,cAAc,KAAK,QAAQ,SAAY,qBAAqB,KAAK,GAAG,IAAI;AAC9E,MAAI,gBAAgB,MAAM;AACxB,UAAM,aAAa,MAAM,oBAAoB,WAAW;AACxD,uBAAmB,WAAW,UAAU;AACxC,WAAO,OAAO,SAAS,eAAe,YAAY,WAAW,IAAI,CAAC;AAAA,EACpE;AACA,SAAO;AAAA,IACL,QAAQ,mBAAmB,SAAS;AAAA,IACpC;AAAA,IACA,KAAK;AAAA,EACP;AACF;AAEA,SAAS,qBACP,OACmB;AACnB,SAAO,OAAO,UAAU,WAAW,EAAE,MAAM,MAAM,IAAI,SAAS,CAAC;AACjE;AAEA,eAAe,4BAA4B,MAA6B;AACtE,MAAI,WAAW,IAAI,EAAG;AACtB,QAAM,SAAS,oBAAoB;AACnC,MAAI,CAAC,WAAW,MAAM,EAAG;AACzB,QAAM,MAAM,MAAM,oBAAoB,MAAM;AAC5C,MAAI,OAAO,KAAK,GAAG,EAAE,WAAW,EAAG;AACnC,QAAM,YAAY,mBAAmB,GAAG,GAAG,IAAI;AACjD;AAEA,SAAS,mBAAmB,KAA4C;AACtE,QAAM,WAAW,cAAc;AAC/B,QAAM,WACJ,IAAI,UAAU,UACd,IAAI,UAAU,QACd,OAAO,IAAI,UAAU,YACrB,CAAC,MAAM,QAAQ,IAAI,KAAK,IACnB,IAAI,QACL,CAAC;AACP,QAAM,aACJ,OAAO,SAAS,YAAY,YAC5B,kBAAkB,SAAS,OAAO,IAC9B,SAAS,UACT,SAAS,MAAM;AACrB,QAAM,YACJ,SAAS,WAAW,UACpB,SAAS,WAAW,QACpB,OAAO,SAAS,WAAW,YAC3B,CAAC,MAAM,QAAQ,SAAS,MAAM,IACzB,SAAS,SACV,CAAC;AACP,QAAM,SAA0D;AAAA,IAC9D,GAAG,SAAS,MAAM;AAAA,EACpB;AACA,aAAW,MAAM,oBAAoB;AACnC,UAAM,QAAQ,UAAU,EAAE;AAC1B,QAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,aAAO,EAAE,IAAI,UAAU,aAAa,UAAU,SAAS,OAAO;AAAA,IAChE,WAAW,UAAU,MAAM;AACzB,aAAO,EAAE,IAAI;AAAA,IACf;AAAA,EACF;AACA,SAAO;AAAA,IACL,iBACE,OAAO,IAAI,oBAAoB,YAC3B,IAAI,kBACJ,SAAS;AAAA,IACf,OAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,mBACP,QACA,YACM;AACN,QAAM,eACJ,WAAW,UAAU,QACrB,OAAO,WAAW,UAAU,YAC5B,CAAC,MAAM,QAAQ,WAAW,KAAK,IAC3B,WAAW,QACX,CAAC;AACP,MAAI,OAAO,KAAK,YAAY,EAAE,WAAW,EAAG;AAC5C,QAAM,cACJ,OAAO,UAAU,QACjB,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,IACvB,OAAO,QACP,CAAC;AACP,SAAO,QAAQ;AACf,MAAI,OAAO,aAAa,YAAY,UAAU;AAC5C,gBAAY,UAAU,aAAa;AAAA,EACrC;AACA,QAAM,gBACJ,aAAa,WAAW,QACxB,OAAO,aAAa,WAAW,YAC/B,CAAC,MAAM,QAAQ,aAAa,MAAM,IAC9B,aAAa,SACb,CAAC;AACP,MAAI,OAAO,KAAK,aAAa,EAAE,WAAW,EAAG;AAC7C,QAAM,eACJ,YAAY,WAAW,QACvB,OAAO,YAAY,WAAW,YAC9B,CAAC,MAAM,QAAQ,YAAY,MAAM,IAC7B,YAAY,SACZ,CAAC;AACP,cAAY,SAAS;AACrB,aAAW,MAAM,oBAAoB;AACnC,QAAI,OAAO,UAAU,eAAe,KAAK,eAAe,EAAE,GAAG;AAC3D,mBAAa,EAAE,IAAI,cAAc,EAAE;AAAA,IACrC;AAAA,EACF;AACF;AAEA,SAAS,eACP,KACA,QACA,YAAY,OACkB;AAC9B,QAAM,UAAwC,CAAC;AAC/C,MAAI,CAAC,aAAa,OAAO,UAAU,eAAe,KAAK,KAAK,iBAAiB,GAAG;AAC9E,YAAQ,kBAAkB;AAAA,EAC5B;AACA,QAAM,QACJ,IAAI,UAAU,QACd,OAAO,IAAI,UAAU,YACrB,CAAC,MAAM,QAAQ,IAAI,KAAK,IACpB,IAAI,QACJ,CAAC;AACP,MAAI,OAAO,UAAU,eAAe,KAAK,OAAO,SAAS,GAAG;AAC1D,YAAQ,eAAe,IAAI;AAAA,EAC7B;AACA,QAAM,SACJ,MAAM,WAAW,QACjB,OAAO,MAAM,WAAW,YACxB,CAAC,MAAM,QAAQ,MAAM,MAAM,IACvB,MAAM,SACN,CAAC;AACP,aAAW,MAAM,oBAAoB;AACnC,QAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,EAAE,GAAG;AACpD,cAAQ,gBAAgB,EAAE,EAAE,IAAI;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,iBAAiB,MAAqC;AACnE,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,SAAS,MAAM,MAAM;AAAA,EACnC,QAAQ;AACN,WAAO,cAAc;AAAA,EACvB;AACA,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,EAAG,QAAO,cAAc;AAC/C,MAAI;AACF,WAAO,mBAAmB,gBAAgB,SAAS,IAAI,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO,cAAc;AAAA,EACvB;AACF;AAEA,eAAsB,YACpB,QACA,MACe;AACf,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,iBAAiB,IAAI;AAC3C,QAAM,cAAc,MAAM,oBAAoB,IAAI;AAClD,QAAM,SAAS,oBAAoB,QAAQ,SAAS,WAAW;AAC/D,QAAM,OAAO,gBAAgB,QAAQ,IAAI;AACzC,QAAM,MAAM,GAAG,IAAI;AACnB,QAAM,UAAU,KAAK,MAAM,MAAM;AACjC,QAAM,OAAO,KAAK,IAAI;AACxB;AAEA,SAAS,gBAAgB,QAA4D;AACnF,QAAM,WAAW,cAAc;AAC/B,SAAO;AAAA,IACL,iBACE,OAAO,OAAO,oBAAoB,YAC9B,OAAO,kBACP,SAAS;AAAA,IACf,OAAO;AAAA,MACL,SACE,OAAO,UAAU,UAAa,kBAAkB,OAAO,MAAM,OAAO,IAChE,OAAO,MAAM,UACb,SAAS,MAAM;AAAA,MACrB,QAAQ;AAAA,QACN,GAAG,SAAS,MAAM;AAAA,QAClB,GAAI,OAAO,OAAO,UAAU,CAAC;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,oBACb,MACkC;AAClC,MAAI;AACF,WAAO,gBAAgB,MAAM,SAAS,MAAM,MAAM,GAAG,IAAI;AAAA,EAC3D,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAEO,SAAS,gBACd,KACA,OAAO,eACkB;AACzB,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,MAAI,KAAK,SAAS,OAAO,KAAK,QAAQ,WAAW,GAAG,GAAG;AACrD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IACzE,SACA,CAAC;AAAA,EACP;AACA,SAAO,gBAAgB,OAAO;AAChC;AAEO,SAAS,gBACd,KACA,OAAO,eACC;AACR,SAAO,KAAK,SAAS,OAAO,IACxB,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA,IAC/B,oBAAoB,GAAG;AAC7B;AAEA,SAAS,gBAAgB,KAAsC;AAC7D,QAAM,SAAkC,CAAC;AACzC,MAAI,UAAoB,CAAC;AACzB,aAAW,YAAY,IAAI,MAAM,OAAO,GAAG;AACzC,UAAM,OAAO,iBAAiB,QAAQ,EAAE,KAAK;AAC7C,QAAI,KAAK,WAAW,EAAG;AACvB,UAAM,eAAe,KAAK,MAAM,yBAAyB;AACzD,QAAI,iBAAiB,MAAM;AACzB,gBAAU,aAAa,CAAC,EAAG,MAAM,GAAG;AACpC;AAAA,IACF;AACA,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,GAAI;AACf,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,UAAM,QAAQ,eAAe,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC;AACtD,kBAAc,QAAQ,CAAC,GAAG,SAAS,GAAG,GAAG,KAAK;AAAA,EAChD;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,KAAsC;AACjE,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,IAAI,oBAAoB,WAAW;AAC5C,UAAM,KAAK,qBAAqB,IAAI,kBAAkB,SAAS,OAAO,EAAE;AAAA,EAC1E;AACA,QAAM,QACJ,IAAI,UAAU,QACd,OAAO,IAAI,UAAU,YACrB,CAAC,MAAM,QAAQ,IAAI,KAAK,IACpB,IAAI,QACJ,CAAC;AACP,MAAI,OAAO,MAAM,YAAY,UAAU;AACrC,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,EAAE;AACnC,UAAM,KAAK,SAAS;AACpB,UAAM,KAAK,aAAa,WAAW,MAAM,OAAO,CAAC,EAAE;AAAA,EACrD;AACA,QAAM,SACJ,MAAM,WAAW,QACjB,OAAO,MAAM,WAAW,YACxB,CAAC,MAAM,QAAQ,MAAM,MAAM,IACvB,MAAM,SACN,CAAC;AACP,QAAM,aAAuB,CAAC;AAC9B,aAAW,MAAM,oBAAoB;AACnC,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,EAAE,EAAG;AACvD,UAAM,QAAQ,OAAO,EAAE,MAAM,OAAO,YAAY,OAAO,EAAE;AACzD,QAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,iBAAW,KAAK,GAAG,EAAE,MAAM,WAAW,KAAK,CAAC,EAAE;AAAA,IAChD;AAAA,EACF;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,EAAE;AACnC,UAAM,KAAK,kBAAkB,GAAG,UAAU;AAAA,EAC5C;AACA,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,MAAI,WAAW;AACf,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,SAAS;AACX,gBAAU;AACV;AAAA,IACF;AACA,QAAI,OAAO,MAAM;AACf,gBAAU;AACV;AAAA,IACF;AACA,QAAI,OAAO,IAAM,YAAW,CAAC;AAC7B,QAAI,OAAO,OAAO,CAAC,SAAU,QAAO,KAAK,MAAM,GAAG,CAAC;AAAA,EACrD;AACA,SAAO;AACT;AAEA,SAAS,eAAe,KAA+B;AACrD,MAAI,QAAQ,OAAQ,QAAO;AAC3B,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,IAAI,WAAW,GAAI,KAAK,IAAI,SAAS,GAAI,GAAG;AAC9C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,cACP,KACA,MACA,OACM;AACN,MAAI,SAAS;AACb,aAAW,QAAQ,KAAK,MAAM,GAAG,EAAE,GAAG;AACpC,UAAM,OAAO,OAAO,IAAI;AACxB,QAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,aAAO,IAAI,IAAI,CAAC;AAAA,IAClB;AACA,aAAS,OAAO,IAAI;AAAA,EACtB;AACA,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,MAAI,SAAS,OAAW,QAAO,IAAI,IAAI;AACzC;AAEA,SAAS,oBACP,QACA,SACA,KACyB;AACzB,QAAM,aAAa,gBAAgB,MAAM;AACzC,QAAM,WAAW,cAAc;AAC/B,QAAM,SAAS,gBAAgB,GAAG;AAElC,MACE,OAAO,oBAAoB,UAC3B,WAAW,oBAAoB,QAAQ,iBACvC;AACA;AAAA,MACE;AAAA,MACA,CAAC,iBAAiB;AAAA,MAClB,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,QAAW;AAC9B,QACE,OAAO,MAAM,YAAY,UACzB,WAAW,MAAM,YAAY,QAAQ,MAAM,SAC3C;AACA;AAAA,QACE;AAAA,QACA,CAAC,SAAS,SAAS;AAAA,QACnB,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,cAAc,OAAO,MAAM,UAAU,CAAC;AAC5C,eAAW,MAAM,oBAAoB;AACnC,UAAI,CAAC,OAAO,UAAU,eAAe,KAAK,aAAa,EAAE,EAAG;AAC5D,YAAM,QAAQ,WAAW,MAAM,OAAO,EAAE,KAAK;AAC7C,YAAM,eAAe,QAAQ,MAAM,OAAO,EAAE,KAAK;AACjD,YAAM,eAAe,SAAS,MAAM,OAAO,EAAE,KAAK;AAClD,UAAI,UAAU,cAAc;AAC1B,uBAAe,QAAQ,CAAC,SAAS,UAAU,EAAE,GAAG,OAAO,YAAY;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AACA,oBAAkB,MAAM;AACxB,SAAO;AACT;AAEA,SAAS,eACP,KACA,MACA,OACA,cACM;AACN,MAAI,SAAS;AACb,aAAW,QAAQ,KAAK,MAAM,GAAG,EAAE,GAAG;AACpC,UAAM,OAAO,OAAO,IAAI;AACxB,QAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,aAAO,IAAI,IAAI,CAAC;AAAA,IAClB;AACA,aAAS,OAAO,IAAI;AAAA,EACtB;AACA,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,MAAI,SAAS,OAAW;AACxB,SAAO,IAAI,IAAI;AACf,MAAI,UAAU,aAAc;AAG9B;AAEA,SAAS,gBAAgB,KAAuD;AAC9E,SAAO,KAAK,MAAM,KAAK,UAAU,GAAG,CAAC;AACvC;AAEA,SAAS,kBAAkB,KAAoC;AAC7D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE;AAAA,IACF;AACA,sBAAkB,KAAgC;AAClD,QAAI,OAAO,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO,IAAI,GAAG;AAAA,EACrD;AACF;","names":[]}
|
|
@@ -4,16 +4,20 @@ import {
|
|
|
4
4
|
} from "./chunk-447U3GQJ.js";
|
|
5
5
|
import {
|
|
6
6
|
UNAUTHENTICATED_MESSAGE,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
buildProviderModelChoices,
|
|
8
|
+
buildProviderSetupView,
|
|
9
|
+
checkClaudeAuth,
|
|
10
|
+
parseAgentSelection
|
|
11
|
+
} from "./chunk-BF2J4XTC.js";
|
|
9
12
|
import {
|
|
10
13
|
isAgentProviderId,
|
|
11
14
|
readConfig,
|
|
12
15
|
writeConfig
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-P5WGG4FJ.js";
|
|
14
17
|
|
|
15
18
|
// src/commands/setup.ts
|
|
16
19
|
import { existsSync as existsSync2 } from "fs";
|
|
20
|
+
import { spawn } from "child_process";
|
|
17
21
|
import {
|
|
18
22
|
copyFile,
|
|
19
23
|
mkdir,
|
|
@@ -227,7 +231,9 @@ async function runSetup(options = {}) {
|
|
|
227
231
|
const agentChoice = await chooseDefaultAgent({
|
|
228
232
|
out,
|
|
229
233
|
interactive,
|
|
230
|
-
requested: options.agent
|
|
234
|
+
requested: options.agent,
|
|
235
|
+
requestedModel: options.model,
|
|
236
|
+
spawnCli: options.spawnCli
|
|
231
237
|
});
|
|
232
238
|
if (!agentChoice.ok) {
|
|
233
239
|
return {
|
|
@@ -237,7 +243,10 @@ async function runSetup(options = {}) {
|
|
|
237
243
|
exitCode: 1
|
|
238
244
|
};
|
|
239
245
|
}
|
|
240
|
-
stepDone(
|
|
246
|
+
stepDone(
|
|
247
|
+
out,
|
|
248
|
+
`Default agent: ${WHITE_BOLD2}${agentChoice.provider}${RST2} (${agentChoice.model ?? "provider default"})`
|
|
249
|
+
);
|
|
241
250
|
out.write(BAR + "\n");
|
|
242
251
|
const ephem = options.installPath !== void 0 ? options.installPath !== null ? detectEphemeral(options.installPath) : false : detectEphemeral(detectCurrentInstallPath());
|
|
243
252
|
if (ephem) {
|
|
@@ -349,28 +358,135 @@ async function safeCheckAuth(spawnCli) {
|
|
|
349
358
|
}
|
|
350
359
|
async function chooseDefaultAgent(args) {
|
|
351
360
|
const config = await readConfig();
|
|
361
|
+
let view = null;
|
|
352
362
|
let selected = args.requested ?? config.agent.default;
|
|
353
|
-
if (args.interactive
|
|
354
|
-
|
|
363
|
+
if (args.interactive || args.requested !== void 0) {
|
|
364
|
+
view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
|
|
365
|
+
}
|
|
366
|
+
if (args.interactive && args.requested === void 0 && view !== null) {
|
|
367
|
+
args.out.write(" Choose default agent:\n");
|
|
368
|
+
view.choices.forEach((choice, index) => {
|
|
369
|
+
const tag = choice.recommended ? " recommended" : "";
|
|
370
|
+
const status = choice.ready ? "ready" : "not ready";
|
|
371
|
+
const detail = choice.account ?? choice.fixCommand ?? choice.detail;
|
|
372
|
+
args.out.write(
|
|
373
|
+
` ${index + 1}. ${choice.label.padEnd(6)} ${status.padEnd(9)}${tag} ${detail}
|
|
374
|
+
`
|
|
375
|
+
);
|
|
376
|
+
});
|
|
377
|
+
selected = (await promptText(
|
|
355
378
|
args.out,
|
|
356
|
-
"
|
|
357
|
-
|
|
358
|
-
);
|
|
379
|
+
"Default agent",
|
|
380
|
+
view.recommendedProvider
|
|
381
|
+
)).toLowerCase();
|
|
382
|
+
const number = Number.parseInt(selected, 10);
|
|
383
|
+
if (Number.isInteger(number) && number >= 1 && number <= view.choices.length) {
|
|
384
|
+
selected = view.choices[number - 1]?.id ?? selected;
|
|
385
|
+
}
|
|
359
386
|
}
|
|
360
|
-
|
|
387
|
+
const parsed = parseAgentSelection(selected);
|
|
388
|
+
if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {
|
|
361
389
|
return {
|
|
362
390
|
ok: false,
|
|
363
391
|
error: `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`
|
|
364
392
|
};
|
|
365
393
|
}
|
|
394
|
+
const provider = parsed.provider;
|
|
395
|
+
let selectedChoice = view?.choices.find((choice) => choice.id === provider);
|
|
396
|
+
if (args.interactive && selectedChoice !== void 0 && !selectedChoice.ready && selectedChoice.fixCommand?.startsWith("run: ") === true) {
|
|
397
|
+
const command = selectedChoice.fixCommand.slice("run: ".length);
|
|
398
|
+
const runLogin = await confirm(
|
|
399
|
+
args.out,
|
|
400
|
+
`${selectedChoice.label} is not ready. Run '${command}' now?`,
|
|
401
|
+
true
|
|
402
|
+
);
|
|
403
|
+
if (runLogin === "install") {
|
|
404
|
+
const login = await runLoginCommand(command);
|
|
405
|
+
if (login.ok) {
|
|
406
|
+
view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
|
|
407
|
+
selectedChoice = view.choices.find((choice) => choice.id === provider);
|
|
408
|
+
} else {
|
|
409
|
+
stepActive(args.out, `${selectedChoice.label} login failed: ${login.error}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const requestedModel = args.requestedModel ?? parsed.model;
|
|
414
|
+
const model = requestedModel ?? await chooseProviderModel({
|
|
415
|
+
out: args.out,
|
|
416
|
+
interactive: args.interactive,
|
|
417
|
+
provider,
|
|
418
|
+
choice: selectedChoice,
|
|
419
|
+
configuredModel: config.agent.models[provider] ?? null
|
|
420
|
+
});
|
|
366
421
|
await writeConfig({
|
|
367
422
|
...config,
|
|
368
423
|
agent: {
|
|
369
424
|
...config.agent,
|
|
370
|
-
default:
|
|
425
|
+
default: provider,
|
|
426
|
+
models: {
|
|
427
|
+
...config.agent.models,
|
|
428
|
+
[provider]: model
|
|
429
|
+
}
|
|
371
430
|
}
|
|
372
431
|
});
|
|
373
|
-
|
|
432
|
+
if (!args.interactive || args.requested !== void 0) {
|
|
433
|
+
const detail = selectedChoice?.ready === true ? "ready" : selectedChoice?.fixCommand ?? selectedChoice?.detail ?? "status unknown";
|
|
434
|
+
stepDone(args.out, `Agent readiness: ${detail}`);
|
|
435
|
+
}
|
|
436
|
+
return { ok: true, provider, model };
|
|
437
|
+
}
|
|
438
|
+
async function chooseProviderModel(args) {
|
|
439
|
+
const choices = args.choice?.modelChoices ?? buildProviderModelChoices(args.provider, args.configuredModel);
|
|
440
|
+
const recommended = choices.find((choice) => choice.recommended) ?? choices.find((choice) => choice.source === "provider-default");
|
|
441
|
+
if (!args.interactive) {
|
|
442
|
+
return args.configuredModel ?? recommended?.value ?? null;
|
|
443
|
+
}
|
|
444
|
+
args.out.write(` Choose ${args.provider} model:
|
|
445
|
+
`);
|
|
446
|
+
choices.forEach((choice, index) => {
|
|
447
|
+
const marker = choice.recommended ? " recommended" : "";
|
|
448
|
+
const current = choice.value === args.configuredModel ? " current" : "";
|
|
449
|
+
args.out.write(
|
|
450
|
+
` ${index + 1}. ${choice.label}${marker}${current}
|
|
451
|
+
`
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
const currentIndex = choices.findIndex(
|
|
455
|
+
(choice) => choice.value === args.configuredModel
|
|
456
|
+
);
|
|
457
|
+
const recommendedIndex = choices.findIndex((choice) => choice.recommended);
|
|
458
|
+
const defaultIndex = currentIndex >= 0 ? currentIndex + 1 : recommendedIndex >= 0 ? recommendedIndex + 1 : 1;
|
|
459
|
+
const selected = await promptText(args.out, "Model", String(defaultIndex));
|
|
460
|
+
const number = Number.parseInt(selected, 10);
|
|
461
|
+
let modelChoice;
|
|
462
|
+
if (Number.isInteger(number) && number >= 1 && number <= choices.length) {
|
|
463
|
+
modelChoice = choices[number - 1];
|
|
464
|
+
} else {
|
|
465
|
+
modelChoice = choices.find((choice) => choice.value === selected);
|
|
466
|
+
}
|
|
467
|
+
if (modelChoice?.source === "custom") {
|
|
468
|
+
const custom = await promptText(args.out, "Custom model id", "");
|
|
469
|
+
return custom.length > 0 ? custom : recommended?.value ?? null;
|
|
470
|
+
}
|
|
471
|
+
return modelChoice?.value ?? recommended?.value ?? null;
|
|
472
|
+
}
|
|
473
|
+
async function runLoginCommand(command) {
|
|
474
|
+
return new Promise((resolve) => {
|
|
475
|
+
const child = spawn(command, {
|
|
476
|
+
shell: true,
|
|
477
|
+
stdio: "inherit"
|
|
478
|
+
});
|
|
479
|
+
child.on("error", (err) => {
|
|
480
|
+
resolve({ ok: false, error: err.message });
|
|
481
|
+
});
|
|
482
|
+
child.on("close", (code) => {
|
|
483
|
+
if (code === 0) {
|
|
484
|
+
resolve({ ok: true });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
resolve({ ok: false, error: `exited ${code ?? 1}` });
|
|
488
|
+
});
|
|
489
|
+
});
|
|
374
490
|
}
|
|
375
491
|
function reportAuth(out, auth) {
|
|
376
492
|
if (auth.loggedIn) {
|
|
@@ -476,7 +592,7 @@ function promptText(out, question, defaultValue) {
|
|
|
476
592
|
if (nl === -1) return;
|
|
477
593
|
process.stdin.removeListener("data", onData);
|
|
478
594
|
process.stdin.pause();
|
|
479
|
-
const answer = buf.slice(0, nl).trim()
|
|
595
|
+
const answer = buf.slice(0, nl).trim();
|
|
480
596
|
resolve(answer.length === 0 ? defaultValue : answer);
|
|
481
597
|
};
|
|
482
598
|
process.stdin.resume();
|
|
@@ -514,4 +630,4 @@ export {
|
|
|
514
630
|
runSetup,
|
|
515
631
|
IMPORT_LINE
|
|
516
632
|
};
|
|
517
|
-
//# sourceMappingURL=chunk-
|
|
633
|
+
//# sourceMappingURL=chunk-QRK3JLFX.js.map
|