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.
Files changed (53) hide show
  1. package/README.md +19 -6
  2. package/dist/agents-4Y7X24WW.js +25 -0
  3. package/dist/chunk-BF2J4XTC.js +766 -0
  4. package/dist/chunk-BF2J4XTC.js.map +1 -0
  5. package/dist/{chunk-B2AGSRXL.js → chunk-CW4HRLMS.js} +88 -7
  6. package/dist/chunk-CW4HRLMS.js.map +1 -0
  7. package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
  8. package/dist/chunk-H37GKBWI.js.map +1 -0
  9. package/dist/{chunk-MX2EW5MR.js → chunk-H6QKCB7M.js} +2 -2
  10. package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
  11. package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
  12. package/dist/chunk-P5WGG4FJ.js +359 -0
  13. package/dist/chunk-P5WGG4FJ.js.map +1 -0
  14. package/dist/{chunk-ZDJSJIB6.js → chunk-QRK3JLFX.js} +131 -15
  15. package/dist/chunk-QRK3JLFX.js.map +1 -0
  16. package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
  17. package/dist/chunk-TILAKDN6.js.map +1 -0
  18. package/dist/chunk-TT6ZP4GS.js +282 -0
  19. package/dist/chunk-TT6ZP4GS.js.map +1 -0
  20. package/dist/chunk-UU6FBRQO.js +187 -0
  21. package/dist/chunk-UU6FBRQO.js.map +1 -0
  22. package/dist/{cli-3XAVBTYG.js → cli-MYMZ66EN.js} +123 -32
  23. package/dist/cli-MYMZ66EN.js.map +1 -0
  24. package/dist/codealmanac.js +1 -1
  25. package/dist/config-ML2RCR7J.js +16 -0
  26. package/dist/{doctor-3BYSF3JD.js → doctor-W5KQQLAX.js} +6 -6
  27. package/dist/{register-commands-7QCIENRZ.js → register-commands-XTK2G2FB.js} +293 -393
  28. package/dist/register-commands-XTK2G2FB.js.map +1 -0
  29. package/dist/{uninstall-FDIOBAAR.js → uninstall-N7JY7ZV2.js} +5 -5
  30. package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
  31. package/guides/mini.md +1 -1
  32. package/guides/reference.md +68 -9
  33. package/package.json +1 -1
  34. package/dist/agents-A4II4YJC.js +0 -15
  35. package/dist/auth-S5DVUIUJ.js +0 -18
  36. package/dist/chunk-B2AGSRXL.js.map +0 -1
  37. package/dist/chunk-P3LDTCLB.js.map +0 -1
  38. package/dist/chunk-R3URPHGH.js +0 -194
  39. package/dist/chunk-R3URPHGH.js.map +0 -1
  40. package/dist/chunk-SSYMRT4I.js +0 -126
  41. package/dist/chunk-SSYMRT4I.js.map +0 -1
  42. package/dist/chunk-V3QOQSXI.js.map +0 -1
  43. package/dist/chunk-WRUSDYYE.js +0 -97
  44. package/dist/chunk-WRUSDYYE.js.map +0 -1
  45. package/dist/chunk-ZDJSJIB6.js.map +0 -1
  46. package/dist/cli-3XAVBTYG.js.map +0 -1
  47. package/dist/register-commands-7QCIENRZ.js.map +0 -1
  48. /package/dist/{agents-A4II4YJC.js.map → agents-4Y7X24WW.js.map} +0 -0
  49. /package/dist/{chunk-MX2EW5MR.js.map → chunk-H6QKCB7M.js.map} +0 -0
  50. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  51. /package/dist/{doctor-3BYSF3JD.js.map → doctor-W5KQQLAX.js.map} +0 -0
  52. /package/dist/{uninstall-FDIOBAAR.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
  53. /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":"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","names":[]}
@@ -4,16 +4,20 @@ import {
4
4
  } from "./chunk-447U3GQJ.js";
5
5
  import {
6
6
  UNAUTHENTICATED_MESSAGE,
7
- checkClaudeAuth
8
- } from "./chunk-SSYMRT4I.js";
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-WRUSDYYE.js";
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(out, `Default agent: ${WHITE_BOLD2}${agentChoice.provider}${RST2}`);
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 && args.requested === void 0) {
354
- selected = await promptText(
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
- "Choose default agent: claude, codex, or cursor",
357
- config.agent.default
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
- if (!isAgentProviderId(selected)) {
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: selected
425
+ default: provider,
426
+ models: {
427
+ ...config.agent.models,
428
+ [provider]: model
429
+ }
371
430
  }
372
431
  });
373
- return { ok: true, provider: selected };
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().toLowerCase();
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-ZDJSJIB6.js.map
633
+ //# sourceMappingURL=chunk-QRK3JLFX.js.map