codealmanac 0.2.3 → 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 (57) 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-HNVOYWC2.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-NBVIEZZQ.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-XNTNXEWY.js → chunk-QRK3JLFX.js} +131 -49
  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-6BOB6KAN.js → cli-MYMZ66EN.js} +123 -33
  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-W5KQQLAX.js +17 -0
  27. package/dist/{register-commands-IXYE5CNZ.js → register-commands-XTK2G2FB.js} +293 -395
  28. package/dist/register-commands-XTK2G2FB.js.map +1 -0
  29. package/dist/uninstall-N7JY7ZV2.js +15 -0
  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-RVYQ44DB.js +0 -16
  35. package/dist/auth-S5DVUIUJ.js +0 -18
  36. package/dist/chunk-HNVOYWC2.js.map +0 -1
  37. package/dist/chunk-P3LDTCLB.js.map +0 -1
  38. package/dist/chunk-PIYJQE4Z.js +0 -102
  39. package/dist/chunk-PIYJQE4Z.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-TWM7I2LU.js +0 -116
  43. package/dist/chunk-TWM7I2LU.js.map +0 -1
  44. package/dist/chunk-V3QOQSXI.js.map +0 -1
  45. package/dist/chunk-WRUSDYYE.js +0 -97
  46. package/dist/chunk-WRUSDYYE.js.map +0 -1
  47. package/dist/chunk-XNTNXEWY.js.map +0 -1
  48. package/dist/cli-6BOB6KAN.js.map +0 -1
  49. package/dist/doctor-DD7EQGCA.js +0 -18
  50. package/dist/register-commands-IXYE5CNZ.js.map +0 -1
  51. package/dist/uninstall-OBV4Z3JE.js +0 -16
  52. /package/dist/{agents-RVYQ44DB.js.map → agents-4Y7X24WW.js.map} +0 -0
  53. /package/dist/{chunk-NBVIEZZQ.js.map → chunk-H6QKCB7M.js.map} +0 -0
  54. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  55. /package/dist/{doctor-DD7EQGCA.js.map → doctor-W5KQQLAX.js.map} +0 -0
  56. /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
  57. /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":[]}
@@ -2,21 +2,22 @@
2
2
  import {
3
3
  runHookInstall
4
4
  } from "./chunk-447U3GQJ.js";
5
- import {
6
- listProviderStatuses
7
- } from "./chunk-TWM7I2LU.js";
8
5
  import {
9
6
  UNAUTHENTICATED_MESSAGE,
10
- checkClaudeAuth
11
- } from "./chunk-SSYMRT4I.js";
7
+ buildProviderModelChoices,
8
+ buildProviderSetupView,
9
+ checkClaudeAuth,
10
+ parseAgentSelection
11
+ } from "./chunk-BF2J4XTC.js";
12
12
  import {
13
13
  isAgentProviderId,
14
14
  readConfig,
15
15
  writeConfig
16
- } from "./chunk-WRUSDYYE.js";
16
+ } from "./chunk-P5WGG4FJ.js";
17
17
 
18
18
  // src/commands/setup.ts
19
19
  import { existsSync as existsSync2 } from "fs";
20
+ import { spawn } from "child_process";
20
21
  import {
21
22
  copyFile,
22
23
  mkdir,
@@ -231,9 +232,8 @@ async function runSetup(options = {}) {
231
232
  out,
232
233
  interactive,
233
234
  requested: options.agent,
234
- spawnCli: options.spawnCli,
235
- listProviderStatuses: options.listProviderStatuses,
236
- promptText: options.promptText ?? promptText
235
+ requestedModel: options.model,
236
+ spawnCli: options.spawnCli
237
237
  });
238
238
  if (!agentChoice.ok) {
239
239
  return {
@@ -243,14 +243,16 @@ async function runSetup(options = {}) {
243
243
  exitCode: 1
244
244
  };
245
245
  }
246
- 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
+ );
247
250
  out.write(BAR + "\n");
248
- const confirmPrompt = options.confirm ?? confirm;
249
251
  const ephem = options.installPath !== void 0 ? options.installPath !== null ? detectEphemeral(options.installPath) : false : detectEphemeral(detectCurrentInstallPath());
250
252
  if (ephem) {
251
253
  let globalAction = "install";
252
254
  if (interactive) {
253
- globalAction = await confirmPrompt(
255
+ globalAction = await confirm(
254
256
  out,
255
257
  `Running from an ephemeral npx location. Install globally so 'almanac' stays on PATH?`,
256
258
  true
@@ -281,7 +283,7 @@ async function runSetup(options = {}) {
281
283
  if (options.skipHook === true) {
282
284
  hookAction = "skip";
283
285
  } else if (interactive) {
284
- hookAction = await confirmPrompt(
286
+ hookAction = await confirm(
285
287
  out,
286
288
  "Install auto-capture hooks for Claude, Codex, and Cursor?",
287
289
  true
@@ -313,7 +315,7 @@ async function runSetup(options = {}) {
313
315
  if (options.skipGuides === true) {
314
316
  guidesAction = "skip";
315
317
  } else if (interactive) {
316
- guidesAction = await confirmPrompt(
318
+ guidesAction = await confirm(
317
319
  out,
318
320
  "Install the codealmanac usage guides into ~/.claude/ and import them from CLAUDE.md?",
319
321
  true
@@ -356,55 +358,135 @@ async function safeCheckAuth(spawnCli) {
356
358
  }
357
359
  async function chooseDefaultAgent(args) {
358
360
  const config = await readConfig();
359
- let statuses;
360
- try {
361
- statuses = args.listProviderStatuses !== void 0 ? await args.listProviderStatuses() : await listProviderStatuses(args.spawnCli);
362
- } catch (err) {
363
- const msg = err instanceof Error ? err.message : String(err);
364
- return { ok: false, error: `failed to check agent providers: ${msg}` };
361
+ let view = null;
362
+ let selected = args.requested ?? config.agent.default;
363
+ if (args.interactive || args.requested !== void 0) {
364
+ view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
365
365
  }
366
- const readyProviders = statuses.filter((status) => status.installed && status.authenticated).map((status) => status.id);
367
- if (readyProviders.length === 0) {
368
- return {
369
- ok: false,
370
- error: "no ready agent providers found. Run `almanac agents list` to see provider readiness."
371
- };
372
- }
373
- const defaultProvider = readyProviders.includes(config.agent.default) ? config.agent.default : readyProviders[0];
374
- let selected = args.requested ?? defaultProvider;
375
- if (args.interactive && args.requested === void 0) {
376
- selected = await args.promptText(
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(
377
378
  args.out,
378
- `Choose default agent: ${formatProviderChoiceList(readyProviders)}`,
379
- defaultProvider
380
- );
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
+ }
381
386
  }
382
- if (!isAgentProviderId(selected)) {
387
+ const parsed = parseAgentSelection(selected);
388
+ if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {
383
389
  return {
384
390
  ok: false,
385
391
  error: `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`
386
392
  };
387
393
  }
388
- const selectedStatus = statuses.find((status) => status.id === selected);
389
- if (selectedStatus === void 0 || !selectedStatus.installed || !selectedStatus.authenticated) {
390
- return {
391
- ok: false,
392
- error: `${selected} not ready: ${selectedStatus?.detail ?? "not available"}. Run \`almanac agents list\` to see provider readiness.`
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
+ }
394
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
+ });
395
421
  await writeConfig({
396
422
  ...config,
397
423
  agent: {
398
424
  ...config.agent,
399
- default: selected
425
+ default: provider,
426
+ models: {
427
+ ...config.agent.models,
428
+ [provider]: model
429
+ }
400
430
  }
401
431
  });
402
- 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;
403
472
  }
404
- function formatProviderChoiceList(providers) {
405
- if (providers.length === 1) return providers[0];
406
- if (providers.length === 2) return `${providers[0]} or ${providers[1]}`;
407
- return `${providers.slice(0, -1).join(", ")}, or ${providers.at(-1)}`;
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
+ });
408
490
  }
409
491
  function reportAuth(out, auth) {
410
492
  if (auth.loggedIn) {
@@ -510,7 +592,7 @@ function promptText(out, question, defaultValue) {
510
592
  if (nl === -1) return;
511
593
  process.stdin.removeListener("data", onData);
512
594
  process.stdin.pause();
513
- const answer = buf.slice(0, nl).trim().toLowerCase();
595
+ const answer = buf.slice(0, nl).trim();
514
596
  resolve(answer.length === 0 ? defaultValue : answer);
515
597
  };
516
598
  process.stdin.resume();
@@ -548,4 +630,4 @@ export {
548
630
  runSetup,
549
631
  IMPORT_LINE
550
632
  };
551
- //# sourceMappingURL=chunk-XNTNXEWY.js.map
633
+ //# sourceMappingURL=chunk-QRK3JLFX.js.map