codealmanac 0.2.6 → 0.2.7

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 (86) hide show
  1. package/LICENSE +21 -133
  2. package/README.md +20 -15
  3. package/dist/{agents-HYRWRHRX.js → agents-V2ZOIACP.js} +6 -5
  4. package/dist/{chunk-PDFS5VFE.js → chunk-447U3GQJ.js} +5 -17
  5. package/dist/chunk-447U3GQJ.js.map +1 -0
  6. package/dist/{chunk-3E7JNMTZ.js → chunk-5BWUMAOX.js} +4 -29
  7. package/dist/chunk-5BWUMAOX.js.map +1 -0
  8. package/dist/{chunk-KQUVMF27.js → chunk-BFIG2CXM.js} +2 -516
  9. package/dist/chunk-BFIG2CXM.js.map +1 -0
  10. package/dist/{chunk-K2JBCB7R.js → chunk-BQY5L3DL.js} +7 -43
  11. package/dist/chunk-BQY5L3DL.js.map +1 -0
  12. package/dist/{chunk-F53U6JQG.js → chunk-CQJVM34R.js} +2 -2
  13. package/dist/chunk-FUBE6KCO.js +124 -0
  14. package/dist/chunk-FUBE6KCO.js.map +1 -0
  15. package/dist/chunk-IZBXXAVL.js +524 -0
  16. package/dist/chunk-IZBXXAVL.js.map +1 -0
  17. package/dist/{chunk-7JUX4ADQ.js → chunk-IZT6RBHS.js} +1 -1
  18. package/dist/{chunk-DW32TL5W.js → chunk-JLQZELHQ.js} +18 -58
  19. package/dist/chunk-JLQZELHQ.js.map +1 -0
  20. package/dist/{chunk-2BNDNGUR.js → chunk-KZXWPG4P.js} +4 -8
  21. package/dist/{chunk-2BNDNGUR.js.map → chunk-KZXWPG4P.js.map} +1 -1
  22. package/dist/{chunk-GPFVEF6V.js → chunk-QIA22IAM.js} +6 -24
  23. package/dist/chunk-QIA22IAM.js.map +1 -0
  24. package/dist/{chunk-J7DNV2DH.js → chunk-RALBM6HZ.js} +43 -355
  25. package/dist/chunk-RALBM6HZ.js.map +1 -0
  26. package/dist/{chunk-HJ3WREGP.js → chunk-U5DLLWIC.js} +3 -3
  27. package/dist/chunk-WL4UE7Q6.js +1386 -0
  28. package/dist/chunk-WL4UE7Q6.js.map +1 -0
  29. package/dist/{chunk-VXDPUOQ5.js → chunk-ZUQN5Y3K.js} +129 -382
  30. package/dist/chunk-ZUQN5Y3K.js.map +1 -0
  31. package/dist/{chunk-ODJAAJGZ.js → chunk-ZZLLOAI6.js} +3 -3
  32. package/dist/{cli-MKXCNEMW.js → cli-XWPNARA6.js} +37 -20
  33. package/dist/cli-XWPNARA6.js.map +1 -0
  34. package/dist/codealmanac.js +1 -1
  35. package/dist/{config-F7FKEQ7F.js → config-KH3JUMG6.js} +4 -4
  36. package/dist/doctor-ENJT665Z.js +18 -0
  37. package/dist/{hook-4SVX446M.js → hook-2NP3UE7U.js} +2 -4
  38. package/dist/paths-O5CZADP2.js +14 -0
  39. package/dist/process-KFSLENL3.js +61 -0
  40. package/dist/{register-commands-2F6SXLDI.js → register-commands-LULZUSPO.js} +999 -1030
  41. package/dist/register-commands-LULZUSPO.js.map +1 -0
  42. package/dist/uninstall-BD4MMQ7M.js +16 -0
  43. package/dist/uninstall-BD4MMQ7M.js.map +1 -0
  44. package/dist/update-XSKPDFMJ.js +11 -0
  45. package/dist/update-XSKPDFMJ.js.map +1 -0
  46. package/dist/{wiki-IGNRNLUZ.js → wiki-O4RWMAE6.js} +8 -6
  47. package/dist/wiki-O4RWMAE6.js.map +1 -0
  48. package/guides/mini.md +8 -6
  49. package/guides/reference.md +89 -32
  50. package/hooks/almanac-capture.sh +7 -8
  51. package/package.json +3 -4
  52. package/prompts/agents/.gitkeep +1 -0
  53. package/prompts/base/notability.md +139 -0
  54. package/prompts/base/purpose.md +85 -0
  55. package/prompts/base/syntax.md +114 -0
  56. package/prompts/operations/absorb.md +43 -0
  57. package/prompts/operations/build.md +49 -0
  58. package/prompts/operations/garden.md +51 -0
  59. package/COMMERCIAL.md +0 -9
  60. package/dist/chunk-3E7JNMTZ.js.map +0 -1
  61. package/dist/chunk-DW32TL5W.js.map +0 -1
  62. package/dist/chunk-GPFVEF6V.js.map +0 -1
  63. package/dist/chunk-J7DNV2DH.js.map +0 -1
  64. package/dist/chunk-K2JBCB7R.js.map +0 -1
  65. package/dist/chunk-KQUVMF27.js.map +0 -1
  66. package/dist/chunk-PDFS5VFE.js.map +0 -1
  67. package/dist/chunk-VXDPUOQ5.js.map +0 -1
  68. package/dist/cli-MKXCNEMW.js.map +0 -1
  69. package/dist/doctor-37UH3HT5.js +0 -17
  70. package/dist/register-commands-2F6SXLDI.js.map +0 -1
  71. package/dist/uninstall-C62ZOK32.js +0 -17
  72. package/dist/update-2UGOFN5C.js +0 -11
  73. package/dist/wiki-IGNRNLUZ.js.map +0 -1
  74. package/prompts/bootstrap.md +0 -176
  75. package/prompts/reviewer.md +0 -129
  76. package/prompts/writer.md +0 -134
  77. /package/dist/{agents-HYRWRHRX.js.map → agents-V2ZOIACP.js.map} +0 -0
  78. /package/dist/{chunk-F53U6JQG.js.map → chunk-CQJVM34R.js.map} +0 -0
  79. /package/dist/{chunk-7JUX4ADQ.js.map → chunk-IZT6RBHS.js.map} +0 -0
  80. /package/dist/{chunk-HJ3WREGP.js.map → chunk-U5DLLWIC.js.map} +0 -0
  81. /package/dist/{chunk-ODJAAJGZ.js.map → chunk-ZZLLOAI6.js.map} +0 -0
  82. /package/dist/{config-F7FKEQ7F.js.map → config-KH3JUMG6.js.map} +0 -0
  83. /package/dist/{doctor-37UH3HT5.js.map → doctor-ENJT665Z.js.map} +0 -0
  84. /package/dist/{hook-4SVX446M.js.map → hook-2NP3UE7U.js.map} +0 -0
  85. /package/dist/{uninstall-C62ZOK32.js.map → paths-O5CZADP2.js.map} +0 -0
  86. /package/dist/{update-2UGOFN5C.js.map → process-KFSLENL3.js.map} +0 -0
@@ -1,113 +1,56 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runHookInstall
4
- } from "./chunk-PDFS5VFE.js";
4
+ } from "./chunk-447U3GQJ.js";
5
5
  import {
6
6
  buildProviderModelChoices,
7
7
  buildProviderSetupView,
8
8
  parseAgentSelection
9
- } from "./chunk-J7DNV2DH.js";
9
+ } from "./chunk-RALBM6HZ.js";
10
+ import {
11
+ UNAUTHENTICATED_MESSAGE,
12
+ checkClaudeAuth
13
+ } from "./chunk-FUBE6KCO.js";
10
14
  import {
11
- disabledAgentProviderMessage,
12
- formatEnabledAgentProviderList,
13
15
  isAgentProviderId,
14
- isEnabledAgentProviderId,
15
16
  readConfig,
16
17
  writeConfig
17
- } from "./chunk-3E7JNMTZ.js";
18
-
19
- // src/agent/providers/codex-instructions.ts
20
- import { existsSync } from "fs";
21
- import { mkdir, readFile, writeFile } from "fs/promises";
22
- import path from "path";
23
- var CODEX_INSTRUCTIONS_START = "<!-- codealmanac:start -->";
24
- var CODEX_INSTRUCTIONS_END = "<!-- codealmanac:end -->";
25
- var CODEX_INSTRUCTIONS_BODY = `${CODEX_INSTRUCTIONS_START}
26
- ## codealmanac
27
-
28
- Use codealmanac before answering codebase questions:
29
- - Search the wiki for relevant context.
30
- - Read matching pages before making claims about the codebase.
31
- - Update the wiki when implementation decisions, workflows, invariants, or gotchas change.
32
-
33
- ${CODEX_INSTRUCTIONS_END}`;
34
- async function ensureCodexInstructions(codexDir) {
35
- await mkdir(codexDir, { recursive: true });
36
- const agentsPath = await resolveCodexAgentsPath(codexDir);
37
- let existing = "";
38
- if (existsSync(agentsPath)) {
39
- existing = await readFile(agentsPath, "utf8");
40
- }
41
- const next = upsertManagedBlock(
42
- existing,
43
- CODEX_INSTRUCTIONS_START,
44
- CODEX_INSTRUCTIONS_END,
45
- CODEX_INSTRUCTIONS_BODY
46
- );
47
- if (next === existing) return false;
48
- await writeFile(agentsPath, next, "utf8");
49
- return true;
50
- }
51
- async function resolveCodexAgentsPath(codexDir) {
52
- const overridePath = path.join(codexDir, "AGENTS.override.md");
53
- if (existsSync(overridePath)) {
54
- try {
55
- const body = await readFile(overridePath, "utf8");
56
- if (body.trim().length > 0) return overridePath;
57
- } catch {
58
- }
59
- }
60
- return path.join(codexDir, "AGENTS.md");
61
- }
62
- function hasCodexInstructions(contents) {
63
- return contents.includes(CODEX_INSTRUCTIONS_START) && contents.includes(CODEX_INSTRUCTIONS_END);
64
- }
65
- function upsertManagedBlock(contents, start, end, block) {
66
- const startIndex = contents.indexOf(start);
67
- const endIndex = contents.indexOf(end);
68
- if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
69
- const afterEnd = endIndex + end.length;
70
- return `${contents.slice(0, startIndex)}${block}${contents.slice(afterEnd)}`;
71
- }
72
- const sep = contents.length === 0 ? "" : contents.endsWith("\n") ? "\n" : "\n\n";
73
- return `${contents}${sep}${block}
74
- `;
75
- }
18
+ } from "./chunk-5BWUMAOX.js";
76
19
 
77
20
  // src/commands/setup.ts
78
- import { existsSync as existsSync3 } from "fs";
21
+ import { existsSync as existsSync2 } from "fs";
79
22
  import { spawn } from "child_process";
80
23
  import {
81
24
  copyFile,
82
- mkdir as mkdir2,
83
- readFile as readFile2,
84
- writeFile as writeFile2
25
+ mkdir,
26
+ readFile,
27
+ writeFile
85
28
  } from "fs/promises";
86
29
  import { createRequire as createRequire2 } from "module";
87
30
  import { homedir as homedir2 } from "os";
88
- import path4 from "path";
31
+ import path3 from "path";
89
32
  import { fileURLToPath as fileURLToPath2 } from "url";
90
33
 
91
34
  // src/commands/setup/install-path.ts
92
35
  import { execFile } from "child_process";
93
36
  import { createRequire } from "module";
94
37
  import { homedir } from "os";
95
- import path2 from "path";
38
+ import path from "path";
96
39
  import { fileURLToPath } from "url";
97
40
  function detectCurrentInstallPath() {
98
41
  try {
99
42
  const req = createRequire(import.meta.url);
100
43
  const here = fileURLToPath(import.meta.url);
101
- let dir = path2.dirname(here);
44
+ let dir = path.dirname(here);
102
45
  for (let i = 0; i < 6; i++) {
103
- const pkgPath = path2.join(dir, "package.json");
46
+ const pkgPath = path.join(dir, "package.json");
104
47
  try {
105
48
  const raw = req("fs").readFileSync(pkgPath, "utf-8");
106
49
  const pkg = JSON.parse(raw);
107
50
  if (pkg.name === "codealmanac") return dir;
108
51
  } catch {
109
52
  }
110
- const parent = path2.dirname(dir);
53
+ const parent = path.dirname(dir);
111
54
  if (parent === dir) break;
112
55
  dir = parent;
113
56
  }
@@ -118,8 +61,8 @@ function detectCurrentInstallPath() {
118
61
  function detectEphemeral(installPath) {
119
62
  if (installPath.length === 0) return false;
120
63
  const home = homedir();
121
- if (installPath.startsWith(path2.join(home, ".npm", "_npx"))) return true;
122
- if (installPath.startsWith(path2.join(home, ".local", "share", "pnpm", "dlx"))) return true;
64
+ if (installPath.startsWith(path.join(home, ".npm", "_npx"))) return true;
65
+ if (installPath.startsWith(path.join(home, ".local", "share", "pnpm", "dlx"))) return true;
123
66
  if (installPath.startsWith("/tmp/")) return true;
124
67
  if (installPath.startsWith("/var/folders/")) return true;
125
68
  return false;
@@ -146,8 +89,8 @@ function spawnGlobalInstall() {
146
89
  }
147
90
 
148
91
  // src/commands/setup/next-steps.ts
149
- import { existsSync as existsSync2, readdirSync } from "fs";
150
- import path3 from "path";
92
+ import { existsSync, readdirSync } from "fs";
93
+ import path2 from "path";
151
94
  var RST = "\x1B[0m";
152
95
  var BOLD = "\x1B[1m";
153
96
  var DIM = "\x1B[2m";
@@ -186,7 +129,7 @@ function printNextSteps(out, existingPageCount) {
186
129
  );
187
130
  out.write(
188
131
  row(
189
- ` ${BLUE}2.${RST} ${BOLD}almanac bootstrap${RST} ${DIM}# scaffold the wiki${RST}`
132
+ ` ${BLUE}2.${RST} ${BOLD}almanac init${RST} ${DIM}# build the wiki${RST}`
190
133
  )
191
134
  );
192
135
  out.write(
@@ -202,8 +145,8 @@ function countExistingPages(cwd) {
202
145
  try {
203
146
  let dir = cwd;
204
147
  for (let i = 0; i < 10; i++) {
205
- const pagesDir = path3.join(dir, ".almanac", "pages");
206
- if (existsSync2(pagesDir)) {
148
+ const pagesDir = path2.join(dir, ".almanac", "pages");
149
+ if (existsSync(pagesDir)) {
207
150
  try {
208
151
  const entries = readdirSync(pagesDir);
209
152
  return entries.filter((e) => e.endsWith(".md")).length;
@@ -211,7 +154,7 @@ function countExistingPages(cwd) {
211
154
  return 0;
212
155
  }
213
156
  }
214
- const parent = path3.dirname(dir);
157
+ const parent = path2.dirname(dir);
215
158
  if (parent === dir) break;
216
159
  dir = parent;
217
160
  }
@@ -251,7 +194,7 @@ function printBanner(out) {
251
194
  `);
252
195
  }
253
196
  out.write(`
254
- ${WHITE_BOLD2} Set up your automatic codebase wiki${RST2}
197
+ ${WHITE_BOLD2} Install the hook + agent guides${RST2}
255
198
  `);
256
199
  }
257
200
  function printBadge(out) {
@@ -284,25 +227,16 @@ async function runSetup(options = {}) {
284
227
  }
285
228
  printBanner(out);
286
229
  printBadge(out);
287
- let agentChoice;
288
- try {
289
- agentChoice = await chooseDefaultAgent({
290
- out,
291
- interactive,
292
- requested: options.agent,
293
- requestedModel: options.model,
294
- spawnCli: options.spawnCli
295
- });
296
- } catch (err) {
297
- if (isSetupInterrupted(err)) {
298
- return {
299
- stdout: "",
300
- stderr: "almanac: setup cancelled\n",
301
- exitCode: 130
302
- };
303
- }
304
- throw err;
305
- }
230
+ const auth = await safeCheckAuth(options.spawnCli);
231
+ reportAuth(out, auth);
232
+ out.write(BAR + "\n");
233
+ const agentChoice = await chooseDefaultAgent({
234
+ out,
235
+ interactive,
236
+ requested: options.agent,
237
+ requestedModel: options.model,
238
+ spawnCli: options.spawnCli
239
+ });
306
240
  if (!agentChoice.ok) {
307
241
  return {
308
242
  stdout: "",
@@ -313,7 +247,7 @@ async function runSetup(options = {}) {
313
247
  }
314
248
  stepDone(
315
249
  out,
316
- `Agent: ${WHITE_BOLD2}${agentChoice.provider}${RST2} (${agentChoice.model ?? "provider default"})`
250
+ `Default agent: ${WHITE_BOLD2}${agentChoice.provider}${RST2} (${agentChoice.model ?? "provider default"})`
317
251
  );
318
252
  out.write(BAR + "\n");
319
253
  const ephem = options.installPath !== void 0 ? options.installPath !== null ? detectEphemeral(options.installPath) : false : detectEphemeral(detectCurrentInstallPath());
@@ -353,7 +287,7 @@ async function runSetup(options = {}) {
353
287
  } else if (interactive) {
354
288
  hookAction = await confirm(
355
289
  out,
356
- "Keep your codebase wiki up to date automatically?",
290
+ "Install auto-capture hooks for Claude, Codex, and Cursor?",
357
291
  true
358
292
  );
359
293
  }
@@ -373,10 +307,10 @@ async function runSetup(options = {}) {
373
307
  exitCode: res.exitCode
374
308
  };
375
309
  }
376
- hookResultLine = res.stdout.includes("already installed") ? `Auto-capture hooks ${DIM2}already installed${RST2}` : `Auto-capture installed`;
310
+ hookResultLine = res.stdout.includes("already installed") ? `Auto-capture hooks ${DIM2}already installed${RST2}` : `Auto-capture hooks installed`;
377
311
  stepDone(out, hookResultLine);
378
312
  } else {
379
- stepSkipped(out, `Auto-capture ${DIM2}skipped${RST2}`);
313
+ stepSkipped(out, `SessionEnd hook ${DIM2}skipped${RST2}`);
380
314
  }
381
315
  out.write(BAR + "\n");
382
316
  let guidesAction = "install";
@@ -385,7 +319,7 @@ async function runSetup(options = {}) {
385
319
  } else if (interactive) {
386
320
  guidesAction = await confirm(
387
321
  out,
388
- "Add codealmanac instructions for your AI agents?",
322
+ "Install the codealmanac usage guides into ~/.claude/ and import them from CLAUDE.md?",
389
323
  true
390
324
  );
391
325
  }
@@ -393,11 +327,10 @@ async function runSetup(options = {}) {
393
327
  if (guidesAction === "install") {
394
328
  try {
395
329
  const summary = await installGuides({
396
- claudeDir: options.claudeDir ?? path4.join(homedir2(), ".claude"),
397
- codexDir: options.codexDir ?? path4.join(homedir2(), ".codex"),
330
+ claudeDir: options.claudeDir ?? path3.join(homedir2(), ".claude"),
398
331
  guidesDir: options.guidesDir ?? resolveGuidesDir()
399
332
  });
400
- guidesSummary = summary.anyChanges ? `Agent instructions added` : `Agent instructions ${DIM2}already added${RST2}`;
333
+ guidesSummary = summary.anyChanges ? `Guides installed (${summary.filesWritten.join(", ")})` : `Guides ${DIM2}already installed${RST2}`;
401
334
  stepDone(out, guidesSummary);
402
335
  } catch (err) {
403
336
  const msg = err instanceof Error ? err.message : String(err);
@@ -409,7 +342,7 @@ async function runSetup(options = {}) {
409
342
  };
410
343
  }
411
344
  } else {
412
- stepSkipped(out, `Agent instructions ${DIM2}skipped${RST2}`);
345
+ stepSkipped(out, `Guides ${DIM2}skipped${RST2}`);
413
346
  }
414
347
  out.write(BAR + "\n");
415
348
  stepDone(out, `${BLUE2}Setup complete${RST2}`);
@@ -418,6 +351,13 @@ async function runSetup(options = {}) {
418
351
  printNextSteps(out, existingPageCount);
419
352
  return { stdout: "", stderr: "", exitCode: 0 };
420
353
  }
354
+ async function safeCheckAuth(spawnCli) {
355
+ try {
356
+ return await checkClaudeAuth(spawnCli);
357
+ } catch {
358
+ return { loggedIn: false };
359
+ }
360
+ }
421
361
  async function chooseDefaultAgent(args) {
422
362
  const config = await readConfig();
423
363
  let view = null;
@@ -426,66 +366,34 @@ async function chooseDefaultAgent(args) {
426
366
  view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
427
367
  }
428
368
  if (args.interactive && args.requested === void 0 && view !== null) {
429
- while (true) {
430
- const choice = await selectChoice({
431
- out: args.out,
432
- title: "Choose your agent",
433
- help: "Choose the AI agent codealmanac should use.",
434
- choices: view.choices.map((choice2) => ({
435
- value: choice2,
436
- line: formatProviderChoice(choice2),
437
- aliases: [choice2.id, choice2.label.toLowerCase()]
438
- })),
439
- defaultIndex: Math.max(
440
- 0,
441
- view.choices.findIndex(
442
- (choice2) => choice2.id === view?.recommendedProvider
443
- )
444
- )
445
- });
446
- if (choice.ready) {
447
- selected = choice.id;
448
- break;
449
- }
450
- if (choice.readiness === "not-authenticated" && choice.fixCommand !== null) {
451
- const command = choice.fixCommand.startsWith("run: ") ? choice.fixCommand.slice("run: ".length) : choice.fixCommand;
452
- const runLogin = await confirm(
453
- args.out,
454
- `${choice.label} sign-in is needed. Run '${command}' now?`,
455
- true
456
- );
457
- if (runLogin === "install") {
458
- const login = await runLoginCommand(command);
459
- if (!login.ok) {
460
- stepActive(args.out, `${choice.label} login failed: ${login.error}`);
461
- }
462
- view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
463
- const refreshed = view.choices.find((next) => next.id === choice.id);
464
- if (refreshed?.ready === true) {
465
- selected = refreshed.id;
466
- break;
467
- }
468
- }
469
- continue;
470
- }
471
- showUnavailableProvider(args.out, choice);
472
- await waitForEnter(args.out, "Press Enter to choose a different agent.");
369
+ args.out.write(" Choose default agent:\n");
370
+ view.choices.forEach((choice, index) => {
371
+ const tag = choice.recommended ? " recommended" : "";
372
+ const status = choice.ready ? "ready" : "not ready";
373
+ const detail = choice.account ?? choice.fixCommand ?? choice.detail;
374
+ args.out.write(
375
+ ` ${index + 1}. ${choice.label.padEnd(6)} ${status.padEnd(9)}${tag} ${detail}
376
+ `
377
+ );
378
+ });
379
+ selected = (await promptText(
380
+ args.out,
381
+ "Default agent",
382
+ view.recommendedProvider
383
+ )).toLowerCase();
384
+ const number = Number.parseInt(selected, 10);
385
+ if (Number.isInteger(number) && number >= 1 && number <= view.choices.length) {
386
+ selected = view.choices[number - 1]?.id ?? selected;
473
387
  }
474
388
  }
475
389
  const parsed = parseAgentSelection(selected);
476
390
  if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {
477
391
  return {
478
392
  ok: false,
479
- error: `unknown agent '${selected}'. Expected one of: ${formatEnabledAgentProviderList()}.`
393
+ error: `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`
480
394
  };
481
395
  }
482
396
  const provider = parsed.provider;
483
- if (!isEnabledAgentProviderId(provider)) {
484
- return {
485
- ok: false,
486
- error: disabledAgentProviderMessage(provider)
487
- };
488
- }
489
397
  let selectedChoice = view?.choices.find((choice) => choice.id === provider);
490
398
  if (args.interactive && selectedChoice !== void 0 && !selectedChoice.ready && selectedChoice.fixCommand?.startsWith("run: ") === true) {
491
399
  const command = selectedChoice.fixCommand.slice("run: ".length);
@@ -504,12 +412,6 @@ async function chooseDefaultAgent(args) {
504
412
  }
505
413
  }
506
414
  }
507
- if (selectedChoice !== void 0 && !selectedChoice.ready) {
508
- return {
509
- ok: false,
510
- error: `${selectedChoice.label} is not ready: ${selectedChoice.fixCommand ?? selectedChoice.detail}`
511
- };
512
- }
513
415
  const requestedModel = args.requestedModel ?? parsed.model;
514
416
  const model = requestedModel ?? await chooseProviderModel({
515
417
  out: args.out,
@@ -529,210 +431,47 @@ async function chooseDefaultAgent(args) {
529
431
  }
530
432
  }
531
433
  });
532
- if ((!args.interactive || args.requested !== void 0) && selectedChoice !== void 0) {
434
+ if (!args.interactive || args.requested !== void 0) {
533
435
  const detail = selectedChoice?.ready === true ? "ready" : selectedChoice?.fixCommand ?? selectedChoice?.detail ?? "status unknown";
534
436
  stepDone(args.out, `Agent readiness: ${detail}`);
535
437
  }
536
438
  return { ok: true, provider, model };
537
439
  }
538
440
  async function chooseProviderModel(args) {
539
- const choices = args.choice?.modelChoices ?? await buildProviderModelChoices(args.provider, args.configuredModel);
441
+ const choices = args.choice?.modelChoices ?? buildProviderModelChoices(args.provider, args.configuredModel);
540
442
  const recommended = choices.find((choice) => choice.recommended) ?? choices.find((choice) => choice.source === "provider-default");
541
443
  if (!args.interactive) {
542
444
  return args.configuredModel ?? recommended?.value ?? null;
543
445
  }
446
+ args.out.write(` Choose ${args.provider} model:
447
+ `);
448
+ choices.forEach((choice, index) => {
449
+ const marker = choice.recommended ? " recommended" : "";
450
+ const current = choice.value === args.configuredModel ? " current" : "";
451
+ args.out.write(
452
+ ` ${index + 1}. ${choice.label}${marker}${current}
453
+ `
454
+ );
455
+ });
544
456
  const currentIndex = choices.findIndex(
545
457
  (choice) => choice.value === args.configuredModel
546
458
  );
547
459
  const recommendedIndex = choices.findIndex((choice) => choice.recommended);
548
- const defaultIndex = Math.max(
549
- 0,
550
- currentIndex >= 0 ? currentIndex : recommendedIndex >= 0 ? recommendedIndex : 0
551
- );
552
- const modelChoice = await selectChoice({
553
- out: args.out,
554
- title: `Choose ${providerDisplayName(args.provider)} model`,
555
- choices: choices.map((choice) => ({
556
- value: choice,
557
- line: formatModelChoice(choice, args.configuredModel),
558
- aliases: choice.value === null ? ["default", "provider default"] : [String(choice.value)]
559
- })),
560
- defaultIndex
561
- });
460
+ const defaultIndex = currentIndex >= 0 ? currentIndex + 1 : recommendedIndex >= 0 ? recommendedIndex + 1 : 1;
461
+ const selected = await promptText(args.out, "Model", String(defaultIndex));
462
+ const number = Number.parseInt(selected, 10);
463
+ let modelChoice;
464
+ if (Number.isInteger(number) && number >= 1 && number <= choices.length) {
465
+ modelChoice = choices[number - 1];
466
+ } else {
467
+ modelChoice = choices.find((choice) => choice.value === selected);
468
+ }
562
469
  if (modelChoice?.source === "custom") {
563
- const custom = await promptText(args.out, "Model name", "");
470
+ const custom = await promptText(args.out, "Custom model id", "");
564
471
  return custom.length > 0 ? custom : recommended?.value ?? null;
565
472
  }
566
473
  return modelChoice?.value ?? recommended?.value ?? null;
567
474
  }
568
- async function selectChoice(args) {
569
- const selected = clampIndex(args.defaultIndex, args.choices.length);
570
- if (canUseRawSelect()) {
571
- return await selectChoiceRaw({ ...args, defaultIndex: selected });
572
- }
573
- renderSelect(args.out, {
574
- title: args.title,
575
- help: args.help,
576
- choices: args.choices,
577
- selected,
578
- raw: false
579
- });
580
- const answer = await promptText(args.out, "Select", String(selected + 1));
581
- const index = Number.parseInt(answer, 10);
582
- if (Number.isInteger(index) && index >= 1 && index <= args.choices.length) {
583
- return args.choices[index - 1].value;
584
- }
585
- const normalized = answer.trim().toLowerCase();
586
- const matched = args.choices.find(
587
- (choice) => choice.aliases?.some((alias) => alias.toLowerCase() === normalized)
588
- );
589
- return (matched ?? args.choices[selected]).value;
590
- }
591
- async function selectChoiceRaw(args) {
592
- return new Promise((resolve, reject) => {
593
- let selected = args.defaultIndex;
594
- let renderedLines = 0;
595
- const input = process.stdin;
596
- const render = () => {
597
- if (renderedLines > 0) {
598
- args.out.write(`\x1B[${renderedLines}A\x1B[0J`);
599
- }
600
- renderedLines = renderSelect(args.out, {
601
- title: args.title,
602
- help: args.help,
603
- choices: args.choices,
604
- selected,
605
- raw: true
606
- });
607
- };
608
- const cleanup = () => {
609
- input.removeListener("data", onData);
610
- input.setRawMode?.(false);
611
- input.pause();
612
- };
613
- const onData = (chunk) => {
614
- const key = chunk.toString("utf8");
615
- if (key === "") {
616
- cleanup();
617
- args.out.write("\n");
618
- reject(new SetupInterruptedError());
619
- return;
620
- }
621
- if (key === "\r" || key === "\n") {
622
- cleanup();
623
- args.out.write("\n");
624
- resolve(args.choices[selected].value);
625
- return;
626
- }
627
- if (key === "\x1B[A") {
628
- selected = selected === 0 ? args.choices.length - 1 : selected - 1;
629
- render();
630
- } else if (key === "\x1B[B") {
631
- selected = selected === args.choices.length - 1 ? 0 : selected + 1;
632
- render();
633
- }
634
- };
635
- input.setRawMode?.(true);
636
- input.resume();
637
- input.on("data", onData);
638
- render();
639
- });
640
- }
641
- function renderSelect(out, args) {
642
- let lines = 0;
643
- out.write(` ${WHITE_BOLD2}${args.title}${RST2}
644
- `);
645
- lines++;
646
- if (args.help !== void 0) {
647
- out.write(` ${DIM2}${args.help}${RST2}
648
- `);
649
- lines++;
650
- }
651
- out.write("\n");
652
- lines++;
653
- args.choices.forEach((choice, index) => {
654
- const pointer = index === args.selected ? `${BLUE2}\u203A${RST2}` : " ";
655
- out.write(` ${pointer} ${choice.line}
656
- `);
657
- lines++;
658
- });
659
- const hint = args.raw ? `Use \u2191/\u2193 to move, Enter to select` : `Type a number or name, then press Enter`;
660
- out.write(`
661
- ${DIM2}${hint}${RST2}
662
- `);
663
- lines += 2;
664
- return lines;
665
- }
666
- var SetupInterruptedError = class extends Error {
667
- constructor() {
668
- super("setup interrupted");
669
- }
670
- };
671
- function isSetupInterrupted(err) {
672
- return err instanceof SetupInterruptedError;
673
- }
674
- function canUseRawSelect() {
675
- const input = process.stdin;
676
- return process.stdin.isTTY === true && typeof input.setRawMode === "function";
677
- }
678
- function clampIndex(index, length) {
679
- if (length <= 0) return 0;
680
- if (index < 0) return 0;
681
- if (index >= length) return length - 1;
682
- return index;
683
- }
684
- function formatProviderChoice(choice) {
685
- const status = providerStatusLabel(choice);
686
- const detail = providerDetailLabel(choice);
687
- const tag = choice.recommended ? ` ${DIM2}recommended${RST2}` : "";
688
- return `${choice.label.padEnd(8)} ${status.padEnd(15)} ${detail}${tag}`;
689
- }
690
- function providerStatusLabel(choice) {
691
- if (choice.ready) {
692
- return choice.detail === "ANTHROPIC_API_KEY set" ? "API key set" : "signed in";
693
- }
694
- return choice.readiness === "missing" ? "not installed" : "sign in needed";
695
- }
696
- function providerDetailLabel(choice) {
697
- if (choice.ready) return choice.account ?? choice.detail;
698
- if (choice.fixCommand === null) return choice.detail;
699
- return choice.fixCommand.startsWith("run: ") ? choice.fixCommand.slice("run: ".length) : choice.fixCommand;
700
- }
701
- function showUnavailableProvider(out, choice) {
702
- if (choice.readiness === "missing") {
703
- out.write(
704
- `
705
- ${WHITE_BOLD2}${choice.label} is not installed.${RST2}
706
- ${providerDetailLabel(choice)}
707
-
708
- `
709
- );
710
- return;
711
- }
712
- out.write(
713
- `
714
- ${WHITE_BOLD2}${choice.label} is not signed in.${RST2}
715
- Run: ${providerDetailLabel(choice)}
716
-
717
- `
718
- );
719
- }
720
- function formatModelChoice(choice, configuredModel) {
721
- const marker = choice.recommended ? ` ${DIM2}recommended${RST2}` : choice.value === configuredModel ? ` ${DIM2}current${RST2}` : "";
722
- const label = choice.source === "provider-default" && choice.value !== null ? friendlyModelLabel(choice.value) : choice.label;
723
- return `${label}${marker}`;
724
- }
725
- function friendlyModelLabel(value) {
726
- if (value === "claude-sonnet-4-6") return "Sonnet 4.6";
727
- if (value === "claude-opus-4-7") return "Opus 4.7";
728
- if (value === "claude-haiku-4-5-20251001") return "Haiku 4.5";
729
- return value;
730
- }
731
- function providerDisplayName(provider) {
732
- if (provider === "claude") return "Claude";
733
- if (provider === "codex") return "Codex";
734
- return "Cursor";
735
- }
736
475
  async function runLoginCommand(command) {
737
476
  return new Promise((resolve) => {
738
477
  const child = spawn(command, {
@@ -751,35 +490,50 @@ async function runLoginCommand(command) {
751
490
  });
752
491
  });
753
492
  }
493
+ function reportAuth(out, auth) {
494
+ if (auth.loggedIn) {
495
+ const who = auth.email ?? "Claude account";
496
+ const plan = auth.subscriptionType !== void 0 ? ` ${DIM2}(${auth.subscriptionType})${RST2}` : "";
497
+ stepDone(out, `Claude auth: ${WHITE_BOLD2}${who}${RST2}${plan}`);
498
+ return;
499
+ }
500
+ if (process.env.ANTHROPIC_API_KEY !== void 0 && process.env.ANTHROPIC_API_KEY.length > 0) {
501
+ stepDone(out, `Claude auth: ${WHITE_BOLD2}ANTHROPIC_API_KEY${RST2} set`);
502
+ return;
503
+ }
504
+ stepActive(out, `Claude auth: ${DIM2}not signed in${RST2}`);
505
+ for (const line of UNAUTHENTICATED_MESSAGE.split("\n")) {
506
+ out.write(` ${DIM2}\u2502 ${line}${RST2}
507
+ `);
508
+ }
509
+ }
754
510
  async function installGuides(options) {
755
- await mkdir2(options.claudeDir, { recursive: true });
756
- const srcMini = path4.join(options.guidesDir, "mini.md");
757
- const srcRef = path4.join(options.guidesDir, "reference.md");
758
- if (!existsSync3(srcMini)) {
511
+ await mkdir(options.claudeDir, { recursive: true });
512
+ const srcMini = path3.join(options.guidesDir, "mini.md");
513
+ const srcRef = path3.join(options.guidesDir, "reference.md");
514
+ if (!existsSync2(srcMini)) {
759
515
  throw new Error(`missing bundled guide: ${srcMini}`);
760
516
  }
761
- if (!existsSync3(srcRef)) {
517
+ if (!existsSync2(srcRef)) {
762
518
  throw new Error(`missing bundled guide: ${srcRef}`);
763
519
  }
764
- const destMini = path4.join(options.claudeDir, "codealmanac.md");
765
- const destRef = path4.join(options.claudeDir, "codealmanac-reference.md");
520
+ const destMini = path3.join(options.claudeDir, "codealmanac.md");
521
+ const destRef = path3.join(options.claudeDir, "codealmanac-reference.md");
766
522
  const miniChanged = await copyIfChanged(srcMini, destMini);
767
523
  const refChanged = await copyIfChanged(srcRef, destRef);
768
- const claudeMd = path4.join(options.claudeDir, "CLAUDE.md");
524
+ const claudeMd = path3.join(options.claudeDir, "CLAUDE.md");
769
525
  const importChanged = await ensureImport(claudeMd);
770
- const codexChanged = await ensureCodexInstructions(options.codexDir);
771
526
  const filesWritten = [];
772
527
  if (miniChanged) filesWritten.push("codealmanac.md");
773
528
  if (refChanged) filesWritten.push("codealmanac-reference.md");
774
529
  if (importChanged) filesWritten.push("CLAUDE.md");
775
- if (codexChanged) filesWritten.push("AGENTS.md");
776
530
  return { anyChanges: filesWritten.length > 0, filesWritten };
777
531
  }
778
532
  async function copyIfChanged(src, dest) {
779
- const srcBytes = await readFile2(src);
780
- if (existsSync3(dest)) {
533
+ const srcBytes = await readFile(src);
534
+ if (existsSync2(dest)) {
781
535
  try {
782
- const destBytes = await readFile2(dest);
536
+ const destBytes = await readFile(dest);
783
537
  if (srcBytes.equals(destBytes)) return false;
784
538
  } catch {
785
539
  }
@@ -790,14 +544,14 @@ async function copyIfChanged(src, dest) {
790
544
  var IMPORT_LINE = "@~/.claude/codealmanac.md";
791
545
  async function ensureImport(claudeMdPath) {
792
546
  let existing = "";
793
- if (existsSync3(claudeMdPath)) {
794
- existing = await readFile2(claudeMdPath, "utf8");
547
+ if (existsSync2(claudeMdPath)) {
548
+ existing = await readFile(claudeMdPath, "utf8");
795
549
  }
796
550
  if (hasImportLine(existing)) return false;
797
551
  const sep = existing.length === 0 ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
798
552
  const body = `${existing}${sep}${IMPORT_LINE}
799
553
  `;
800
- await writeFile2(claudeMdPath, body, "utf8");
554
+ await writeFile(claudeMdPath, body, "utf8");
801
555
  return true;
802
556
  }
803
557
  function hasImportLine(contents) {
@@ -847,17 +601,14 @@ function promptText(out, question, defaultValue) {
847
601
  process.stdin.on("data", onData);
848
602
  });
849
603
  }
850
- async function waitForEnter(out, message) {
851
- await promptText(out, message, "");
852
- }
853
604
  function resolveGuidesDir() {
854
- const here = path4.dirname(fileURLToPath2(import.meta.url));
605
+ const here = path3.dirname(fileURLToPath2(import.meta.url));
855
606
  const candidates = [
856
- path4.resolve(here, "..", "guides"),
607
+ path3.resolve(here, "..", "guides"),
857
608
  // dist layout
858
- path4.resolve(here, "..", "..", "guides"),
609
+ path3.resolve(here, "..", "..", "guides"),
859
610
  // src layout
860
- path4.resolve(here, "..", "..", "..", "guides")
611
+ path3.resolve(here, "..", "..", "..", "guides")
861
612
  ];
862
613
  for (const dir of candidates) {
863
614
  if (looksLikeGuidesDir(dir)) return dir;
@@ -865,7 +616,7 @@ function resolveGuidesDir() {
865
616
  try {
866
617
  const require2 = createRequire2(import.meta.url);
867
618
  const pkgJson = require2.resolve("codealmanac/package.json");
868
- const guides = path4.join(path4.dirname(pkgJson), "guides");
619
+ const guides = path3.join(path3.dirname(pkgJson), "guides");
869
620
  if (looksLikeGuidesDir(guides)) return guides;
870
621
  } catch {
871
622
  }
@@ -874,15 +625,11 @@ function resolveGuidesDir() {
874
625
  );
875
626
  }
876
627
  function looksLikeGuidesDir(dir) {
877
- return existsSync3(path4.join(dir, "mini.md"));
628
+ return existsSync2(path3.join(dir, "mini.md"));
878
629
  }
879
630
 
880
631
  export {
881
- CODEX_INSTRUCTIONS_START,
882
- CODEX_INSTRUCTIONS_END,
883
- resolveCodexAgentsPath,
884
- hasCodexInstructions,
885
632
  runSetup,
886
633
  IMPORT_LINE
887
634
  };
888
- //# sourceMappingURL=chunk-VXDPUOQ5.js.map
635
+ //# sourceMappingURL=chunk-ZUQN5Y3K.js.map