codealmanac 0.2.3 → 0.2.5

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 (58) hide show
  1. package/README.md +27 -14
  2. package/dist/agents-RVTQYE6A.js +25 -0
  3. package/dist/chunk-6BJUYZ43.js +195 -0
  4. package/dist/chunk-6BJUYZ43.js.map +1 -0
  5. package/dist/chunk-BGUID5BS.js +766 -0
  6. package/dist/chunk-BGUID5BS.js.map +1 -0
  7. package/dist/{chunk-NBVIEZZQ.js → chunk-DL5BXZCX.js} +53 -3
  8. package/dist/chunk-DL5BXZCX.js.map +1 -0
  9. package/dist/{chunk-XNTNXEWY.js → chunk-GFUB57IT.js} +243 -83
  10. package/dist/chunk-GFUB57IT.js.map +1 -0
  11. package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
  12. package/dist/chunk-H37GKBWI.js.map +1 -0
  13. package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
  14. package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
  15. package/dist/chunk-P5WGG4FJ.js +359 -0
  16. package/dist/chunk-P5WGG4FJ.js.map +1 -0
  17. package/dist/{chunk-HNVOYWC2.js → chunk-SMIK2YLU.js} +165 -76
  18. package/dist/chunk-SMIK2YLU.js.map +1 -0
  19. package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
  20. package/dist/chunk-TILAKDN6.js.map +1 -0
  21. package/dist/chunk-TT6ZP4GS.js +282 -0
  22. package/dist/chunk-TT6ZP4GS.js.map +1 -0
  23. package/dist/{cli-6BOB6KAN.js → cli-CL4ID7EO.js} +123 -33
  24. package/dist/cli-CL4ID7EO.js.map +1 -0
  25. package/dist/codealmanac.js +1 -1
  26. package/dist/config-ML2RCR7J.js +16 -0
  27. package/dist/doctor-DOLJRGS4.js +17 -0
  28. package/dist/{register-commands-IXYE5CNZ.js → register-commands-FBJ6XQ3L.js} +296 -398
  29. package/dist/register-commands-FBJ6XQ3L.js.map +1 -0
  30. package/dist/uninstall-DX6LFKMX.js +15 -0
  31. package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
  32. package/guides/mini.md +4 -4
  33. package/guides/reference.md +75 -16
  34. package/package.json +1 -1
  35. package/dist/agents-RVYQ44DB.js +0 -16
  36. package/dist/auth-S5DVUIUJ.js +0 -18
  37. package/dist/chunk-HNVOYWC2.js.map +0 -1
  38. package/dist/chunk-NBVIEZZQ.js.map +0 -1
  39. package/dist/chunk-P3LDTCLB.js.map +0 -1
  40. package/dist/chunk-PIYJQE4Z.js +0 -102
  41. package/dist/chunk-PIYJQE4Z.js.map +0 -1
  42. package/dist/chunk-SSYMRT4I.js +0 -126
  43. package/dist/chunk-SSYMRT4I.js.map +0 -1
  44. package/dist/chunk-TWM7I2LU.js +0 -116
  45. package/dist/chunk-TWM7I2LU.js.map +0 -1
  46. package/dist/chunk-V3QOQSXI.js.map +0 -1
  47. package/dist/chunk-WRUSDYYE.js +0 -97
  48. package/dist/chunk-WRUSDYYE.js.map +0 -1
  49. package/dist/chunk-XNTNXEWY.js.map +0 -1
  50. package/dist/cli-6BOB6KAN.js.map +0 -1
  51. package/dist/doctor-DD7EQGCA.js +0 -18
  52. package/dist/register-commands-IXYE5CNZ.js.map +0 -1
  53. package/dist/uninstall-OBV4Z3JE.js +0 -16
  54. /package/dist/{agents-RVYQ44DB.js.map → agents-RVTQYE6A.js.map} +0 -0
  55. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  56. /package/dist/{doctor-DD7EQGCA.js.map → doctor-DOLJRGS4.js.map} +0 -0
  57. /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-DX6LFKMX.js.map} +0 -0
  58. /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
@@ -3,20 +3,20 @@ import {
3
3
  runHookInstall
4
4
  } from "./chunk-447U3GQJ.js";
5
5
  import {
6
- listProviderStatuses
7
- } from "./chunk-TWM7I2LU.js";
8
- import {
9
- UNAUTHENTICATED_MESSAGE,
10
- checkClaudeAuth
11
- } from "./chunk-SSYMRT4I.js";
6
+ buildProviderModelChoices,
7
+ buildProviderSetupView,
8
+ getProviderLabel,
9
+ parseAgentSelection
10
+ } from "./chunk-BGUID5BS.js";
12
11
  import {
13
12
  isAgentProviderId,
14
13
  readConfig,
15
14
  writeConfig
16
- } from "./chunk-WRUSDYYE.js";
15
+ } from "./chunk-P5WGG4FJ.js";
17
16
 
18
17
  // src/commands/setup.ts
19
18
  import { existsSync as existsSync2 } from "fs";
19
+ import { spawn } from "child_process";
20
20
  import {
21
21
  copyFile,
22
22
  mkdir,
@@ -191,7 +191,7 @@ function printBanner(out) {
191
191
  `);
192
192
  }
193
193
  out.write(`
194
- ${WHITE_BOLD2} Install the hook + agent guides${RST2}
194
+ ${WHITE_BOLD2} Install provider integrations${RST2}
195
195
  `);
196
196
  }
197
197
  function printBadge(out) {
@@ -224,16 +224,12 @@ async function runSetup(options = {}) {
224
224
  }
225
225
  printBanner(out);
226
226
  printBadge(out);
227
- const auth = await safeCheckAuth(options.spawnCli);
228
- reportAuth(out, auth);
229
- out.write(BAR + "\n");
230
227
  const agentChoice = await chooseDefaultAgent({
231
228
  out,
232
229
  interactive,
233
230
  requested: options.agent,
234
- spawnCli: options.spawnCli,
235
- listProviderStatuses: options.listProviderStatuses,
236
- promptText: options.promptText ?? promptText
231
+ requestedModel: options.model,
232
+ spawnCli: options.spawnCli
237
233
  });
238
234
  if (!agentChoice.ok) {
239
235
  return {
@@ -243,14 +239,16 @@ async function runSetup(options = {}) {
243
239
  exitCode: 1
244
240
  };
245
241
  }
246
- stepDone(out, `Default agent: ${WHITE_BOLD2}${agentChoice.provider}${RST2}`);
242
+ stepDone(
243
+ out,
244
+ `Default provider: ${WHITE_BOLD2}${agentChoice.provider}${RST2} (${agentChoice.model ?? "provider default"})`
245
+ );
247
246
  out.write(BAR + "\n");
248
- const confirmPrompt = options.confirm ?? confirm;
249
247
  const ephem = options.installPath !== void 0 ? options.installPath !== null ? detectEphemeral(options.installPath) : false : detectEphemeral(detectCurrentInstallPath());
250
248
  if (ephem) {
251
249
  let globalAction = "install";
252
250
  if (interactive) {
253
- globalAction = await confirmPrompt(
251
+ globalAction = await confirm(
254
252
  out,
255
253
  `Running from an ephemeral npx location. Install globally so 'almanac' stays on PATH?`,
256
254
  true
@@ -281,9 +279,9 @@ async function runSetup(options = {}) {
281
279
  if (options.skipHook === true) {
282
280
  hookAction = "skip";
283
281
  } else if (interactive) {
284
- hookAction = await confirmPrompt(
282
+ hookAction = await confirm(
285
283
  out,
286
- "Install auto-capture hooks for Claude, Codex, and Cursor?",
284
+ "Install auto-capture hooks for supported agents?",
287
285
  true
288
286
  );
289
287
  }
@@ -296,7 +294,7 @@ async function runSetup(options = {}) {
296
294
  stableHooksDir: options.stableHooksDir
297
295
  });
298
296
  if (res.exitCode !== 0) {
299
- stepActive(out, `SessionEnd hook: ${res.stderr.trim()}`);
297
+ stepActive(out, `Auto-capture hook: ${res.stderr.trim()}`);
300
298
  return {
301
299
  stdout: "",
302
300
  stderr: res.stderr,
@@ -306,16 +304,17 @@ async function runSetup(options = {}) {
306
304
  hookResultLine = res.stdout.includes("already installed") ? `Auto-capture hooks ${DIM2}already installed${RST2}` : `Auto-capture hooks installed`;
307
305
  stepDone(out, hookResultLine);
308
306
  } else {
309
- stepSkipped(out, `SessionEnd hook ${DIM2}skipped${RST2}`);
307
+ stepSkipped(out, `Auto-capture hooks ${DIM2}skipped${RST2}`);
310
308
  }
311
309
  out.write(BAR + "\n");
312
310
  let guidesAction = "install";
311
+ const providerLabel = getProviderLabel(agentChoice.provider);
313
312
  if (options.skipGuides === true) {
314
313
  guidesAction = "skip";
315
314
  } else if (interactive) {
316
- guidesAction = await confirmPrompt(
315
+ guidesAction = await confirm(
317
316
  out,
318
- "Install the codealmanac usage guides into ~/.claude/ and import them from CLAUDE.md?",
317
+ `Install the codealmanac guide for ${providerLabel}?`,
319
318
  true
320
319
  );
321
320
  }
@@ -323,10 +322,12 @@ async function runSetup(options = {}) {
323
322
  if (guidesAction === "install") {
324
323
  try {
325
324
  const summary = await installGuides({
325
+ provider: agentChoice.provider,
326
326
  claudeDir: options.claudeDir ?? path3.join(homedir2(), ".claude"),
327
- guidesDir: options.guidesDir ?? resolveGuidesDir()
327
+ guidesDir: options.guidesDir ?? resolveGuidesDir(),
328
+ cwd: process.cwd()
328
329
  });
329
- guidesSummary = summary.anyChanges ? `Guides installed (${summary.filesWritten.join(", ")})` : `Guides ${DIM2}already installed${RST2}`;
330
+ guidesSummary = summary.anyChanges ? `Guide installed for ${summary.providerLabel} (${summary.filesWritten.join(", ")})` : `Guide for ${summary.providerLabel} ${DIM2}already installed${RST2}`;
330
331
  stepDone(out, guidesSummary);
331
332
  } catch (err) {
332
333
  const msg = err instanceof Error ? err.message : String(err);
@@ -345,86 +346,144 @@ async function runSetup(options = {}) {
345
346
  out.write("\n");
346
347
  const existingPageCount = countExistingPages(process.cwd());
347
348
  printNextSteps(out, existingPageCount);
349
+ printProviderNextSteps(out);
348
350
  return { stdout: "", stderr: "", exitCode: 0 };
349
351
  }
350
- async function safeCheckAuth(spawnCli) {
351
- try {
352
- return await checkClaudeAuth(spawnCli);
353
- } catch {
354
- return { loggedIn: false };
355
- }
356
- }
357
352
  async function chooseDefaultAgent(args) {
358
353
  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}` };
354
+ let view = null;
355
+ let selected = args.requested ?? config.agent.default;
356
+ if (args.interactive || args.requested !== void 0) {
357
+ view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
365
358
  }
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(
359
+ if (args.interactive && args.requested === void 0 && view !== null) {
360
+ args.out.write(" Choose default provider for bootstrap/capture:\n");
361
+ view.choices.forEach((choice, index) => {
362
+ const tag = choice.recommended ? " recommended" : "";
363
+ const status = choice.ready ? "ready" : "not ready";
364
+ const detail = choice.account ?? choice.fixCommand ?? choice.detail;
365
+ args.out.write(
366
+ ` ${index + 1}. ${choice.label.padEnd(6)} ${status.padEnd(9)}${tag} ${detail}
367
+ `
368
+ );
369
+ });
370
+ selected = (await promptText(
377
371
  args.out,
378
- `Choose default agent: ${formatProviderChoiceList(readyProviders)}`,
379
- defaultProvider
380
- );
372
+ "Default provider",
373
+ view.recommendedProvider
374
+ )).toLowerCase();
375
+ const number = Number.parseInt(selected, 10);
376
+ if (Number.isInteger(number) && number >= 1 && number <= view.choices.length) {
377
+ selected = view.choices[number - 1]?.id ?? selected;
378
+ }
381
379
  }
382
- if (!isAgentProviderId(selected)) {
380
+ const parsed = parseAgentSelection(selected);
381
+ if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {
383
382
  return {
384
383
  ok: false,
385
384
  error: `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`
386
385
  };
387
386
  }
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
- };
387
+ const provider = parsed.provider;
388
+ let selectedChoice = view?.choices.find((choice) => choice.id === provider);
389
+ if (args.interactive && selectedChoice !== void 0 && !selectedChoice.ready && selectedChoice.fixCommand?.startsWith("run: ") === true) {
390
+ const command = selectedChoice.fixCommand.slice("run: ".length);
391
+ const runLogin = await confirm(
392
+ args.out,
393
+ `${selectedChoice.label} is not ready. Run '${command}' now?`,
394
+ true
395
+ );
396
+ if (runLogin === "install") {
397
+ const login = await runLoginCommand(command);
398
+ if (login.ok) {
399
+ view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });
400
+ selectedChoice = view.choices.find((choice) => choice.id === provider);
401
+ } else {
402
+ stepActive(args.out, `${selectedChoice.label} login failed: ${login.error}`);
403
+ }
404
+ }
394
405
  }
406
+ const requestedModel = args.requestedModel ?? parsed.model;
407
+ const model = requestedModel ?? await chooseProviderModel({
408
+ out: args.out,
409
+ interactive: args.interactive,
410
+ provider,
411
+ choice: selectedChoice,
412
+ configuredModel: config.agent.models[provider] ?? null
413
+ });
395
414
  await writeConfig({
396
415
  ...config,
397
416
  agent: {
398
417
  ...config.agent,
399
- default: selected
418
+ default: provider,
419
+ models: {
420
+ ...config.agent.models,
421
+ [provider]: model
422
+ }
400
423
  }
401
424
  });
402
- return { ok: true, provider: selected };
403
- }
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)}`;
408
- }
409
- function reportAuth(out, auth) {
410
- if (auth.loggedIn) {
411
- const who = auth.email ?? "Claude account";
412
- const plan = auth.subscriptionType !== void 0 ? ` ${DIM2}(${auth.subscriptionType})${RST2}` : "";
413
- stepDone(out, `Claude auth: ${WHITE_BOLD2}${who}${RST2}${plan}`);
414
- return;
425
+ if (!args.interactive || args.requested !== void 0) {
426
+ const detail = selectedChoice?.ready === true ? "ready" : selectedChoice?.fixCommand ?? selectedChoice?.detail ?? "status unknown";
427
+ stepDone(args.out, `Agent readiness: ${detail}`);
415
428
  }
416
- if (process.env.ANTHROPIC_API_KEY !== void 0 && process.env.ANTHROPIC_API_KEY.length > 0) {
417
- stepDone(out, `Claude auth: ${WHITE_BOLD2}ANTHROPIC_API_KEY${RST2} set`);
418
- return;
429
+ return { ok: true, provider, model };
430
+ }
431
+ async function chooseProviderModel(args) {
432
+ const choices = args.choice?.modelChoices ?? buildProviderModelChoices(args.provider, args.configuredModel);
433
+ const recommended = choices.find((choice) => choice.recommended) ?? choices.find((choice) => choice.source === "provider-default");
434
+ if (!args.interactive) {
435
+ return args.configuredModel ?? recommended?.value ?? null;
419
436
  }
420
- stepActive(out, `Claude auth: ${DIM2}not signed in${RST2}`);
421
- for (const line of UNAUTHENTICATED_MESSAGE.split("\n")) {
422
- out.write(` ${DIM2}\u2502 ${line}${RST2}
437
+ args.out.write(` Choose model for ${getProviderLabel(args.provider)}:
423
438
  `);
439
+ choices.forEach((choice, index) => {
440
+ const marker = choice.recommended ? " recommended" : "";
441
+ const current = choice.value === args.configuredModel ? " current" : "";
442
+ args.out.write(
443
+ ` ${index + 1}. ${choice.label}${marker}${current}
444
+ `
445
+ );
446
+ });
447
+ const currentIndex = choices.findIndex(
448
+ (choice) => choice.value === args.configuredModel
449
+ );
450
+ const recommendedIndex = choices.findIndex((choice) => choice.recommended);
451
+ const defaultIndex = currentIndex >= 0 ? currentIndex + 1 : recommendedIndex >= 0 ? recommendedIndex + 1 : 1;
452
+ const selected = await promptText(args.out, "Model", String(defaultIndex));
453
+ const number = Number.parseInt(selected, 10);
454
+ let modelChoice;
455
+ if (Number.isInteger(number) && number >= 1 && number <= choices.length) {
456
+ modelChoice = choices[number - 1];
457
+ } else {
458
+ modelChoice = choices.find((choice) => choice.value === selected);
424
459
  }
460
+ if (modelChoice?.source === "custom") {
461
+ const custom = await promptText(args.out, "Custom model id", "");
462
+ return custom.length > 0 ? custom : recommended?.value ?? null;
463
+ }
464
+ return modelChoice?.value ?? recommended?.value ?? null;
465
+ }
466
+ async function runLoginCommand(command) {
467
+ return new Promise((resolve) => {
468
+ const child = spawn(command, {
469
+ shell: true,
470
+ stdio: "inherit"
471
+ });
472
+ child.on("error", (err) => {
473
+ resolve({ ok: false, error: err.message });
474
+ });
475
+ child.on("close", (code) => {
476
+ if (code === 0) {
477
+ resolve({ ok: true });
478
+ return;
479
+ }
480
+ resolve({ ok: false, error: `exited ${code ?? 1}` });
481
+ });
482
+ });
425
483
  }
484
+ var CODEX_BLOCK_START = "<!-- codealmanac:start -->";
485
+ var CODEX_BLOCK_END = "<!-- codealmanac:end -->";
426
486
  async function installGuides(options) {
427
- await mkdir(options.claudeDir, { recursive: true });
428
487
  const srcMini = path3.join(options.guidesDir, "mini.md");
429
488
  const srcRef = path3.join(options.guidesDir, "reference.md");
430
489
  if (!existsSync2(srcMini)) {
@@ -433,6 +492,17 @@ async function installGuides(options) {
433
492
  if (!existsSync2(srcRef)) {
434
493
  throw new Error(`missing bundled guide: ${srcRef}`);
435
494
  }
495
+ switch (options.provider) {
496
+ case "claude":
497
+ return await installClaudeGuides(options, srcMini, srcRef);
498
+ case "codex":
499
+ return await installCodexGuides(srcMini, srcRef);
500
+ case "cursor":
501
+ return await installCursorGuides(options.cwd, srcMini);
502
+ }
503
+ }
504
+ async function installClaudeGuides(options, srcMini, srcRef) {
505
+ await mkdir(options.claudeDir, { recursive: true });
436
506
  const destMini = path3.join(options.claudeDir, "codealmanac.md");
437
507
  const destRef = path3.join(options.claudeDir, "codealmanac-reference.md");
438
508
  const miniChanged = await copyIfChanged(srcMini, destMini);
@@ -443,7 +513,45 @@ async function installGuides(options) {
443
513
  if (miniChanged) filesWritten.push("codealmanac.md");
444
514
  if (refChanged) filesWritten.push("codealmanac-reference.md");
445
515
  if (importChanged) filesWritten.push("CLAUDE.md");
446
- return { anyChanges: filesWritten.length > 0, filesWritten };
516
+ return {
517
+ providerLabel: "Claude",
518
+ anyChanges: filesWritten.length > 0,
519
+ filesWritten
520
+ };
521
+ }
522
+ async function installCodexGuides(srcMini, srcRef) {
523
+ const codexDir = path3.join(homedir2(), ".codex");
524
+ await mkdir(codexDir, { recursive: true });
525
+ const destMini = path3.join(codexDir, "codealmanac.md");
526
+ const destRef = path3.join(codexDir, "codealmanac-reference.md");
527
+ const miniChanged = await copyIfChanged(srcMini, destMini);
528
+ const refChanged = await copyIfChanged(srcRef, destRef);
529
+ const agentsChanged = await ensureManagedBlock(
530
+ path3.join(codexDir, "AGENTS.md"),
531
+ codexGuideBlock()
532
+ );
533
+ const filesWritten = [];
534
+ if (miniChanged) filesWritten.push("codealmanac.md");
535
+ if (refChanged) filesWritten.push("codealmanac-reference.md");
536
+ if (agentsChanged) filesWritten.push("AGENTS.md");
537
+ return {
538
+ providerLabel: "Codex",
539
+ anyChanges: filesWritten.length > 0,
540
+ filesWritten
541
+ };
542
+ }
543
+ async function installCursorGuides(cwd, srcMini) {
544
+ const rulesDir = path3.join(cwd, ".cursor", "rules");
545
+ await mkdir(rulesDir, { recursive: true });
546
+ const mini = await readFile(srcMini, "utf8");
547
+ const dest = path3.join(rulesDir, "codealmanac.mdc");
548
+ const body = "---\ndescription: Use codealmanac wiki context during coding work\nalwaysApply: true\n---\n\n" + mini;
549
+ const changed = await writeIfChanged(dest, body);
550
+ return {
551
+ providerLabel: "Cursor",
552
+ anyChanges: changed,
553
+ filesWritten: changed ? [".cursor/rules/codealmanac.mdc"] : []
554
+ };
447
555
  }
448
556
  async function copyIfChanged(src, dest) {
449
557
  const srcBytes = await readFile(src);
@@ -457,6 +565,45 @@ async function copyIfChanged(src, dest) {
457
565
  await copyFile(src, dest);
458
566
  return true;
459
567
  }
568
+ async function writeIfChanged(dest, body) {
569
+ if (existsSync2(dest)) {
570
+ try {
571
+ if (await readFile(dest, "utf8") === body) return false;
572
+ } catch {
573
+ }
574
+ }
575
+ await writeFile(dest, body, "utf8");
576
+ return true;
577
+ }
578
+ async function ensureManagedBlock(file, block) {
579
+ let existing = "";
580
+ if (existsSync2(file)) existing = await readFile(file, "utf8");
581
+ const pattern = new RegExp(
582
+ `${escapeRegex(CODEX_BLOCK_START)}[\\s\\S]*?${escapeRegex(CODEX_BLOCK_END)}`
583
+ );
584
+ const next = pattern.test(existing) ? existing.replace(pattern, block.trimEnd()) : `${existing.trimEnd()}${existing.trim().length > 0 ? "\n\n" : ""}${block.trimEnd()}
585
+ `;
586
+ const normalized = next.endsWith("\n") ? next : `${next}
587
+ `;
588
+ if (normalized === existing) return false;
589
+ await writeFile(file, normalized, "utf8");
590
+ return true;
591
+ }
592
+ function codexGuideBlock() {
593
+ return [
594
+ CODEX_BLOCK_START,
595
+ "# codealmanac",
596
+ "",
597
+ "This machine has codealmanac installed: a local wiki for codebases.",
598
+ "Before codealmanac/wiki-related work, read `~/.codex/codealmanac.md`.",
599
+ "For the full command reference, read `~/.codex/codealmanac-reference.md` on demand.",
600
+ CODEX_BLOCK_END,
601
+ ""
602
+ ].join("\n");
603
+ }
604
+ function escapeRegex(value) {
605
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
606
+ }
460
607
  var IMPORT_LINE = "@~/.claude/codealmanac.md";
461
608
  async function ensureImport(claudeMdPath) {
462
609
  let existing = "";
@@ -510,7 +657,7 @@ function promptText(out, question, defaultValue) {
510
657
  if (nl === -1) return;
511
658
  process.stdin.removeListener("data", onData);
512
659
  process.stdin.pause();
513
- const answer = buf.slice(0, nl).trim().toLowerCase();
660
+ const answer = buf.slice(0, nl).trim();
514
661
  resolve(answer.length === 0 ? defaultValue : answer);
515
662
  };
516
663
  process.stdin.resume();
@@ -543,9 +690,22 @@ function resolveGuidesDir() {
543
690
  function looksLikeGuidesDir(dir) {
544
691
  return existsSync2(path3.join(dir, "mini.md"));
545
692
  }
693
+ function printProviderNextSteps(out) {
694
+ out.write(` ${DIM2}Change provider/model later:${RST2}
695
+ `);
696
+ out.write(` ${DIM2} almanac agents list${RST2}
697
+ `);
698
+ out.write(` ${DIM2} almanac agents use <claude|codex|cursor>${RST2}
699
+ `);
700
+ out.write(
701
+ ` ${DIM2} almanac agents model <provider> <model|--default>${RST2}
702
+
703
+ `
704
+ );
705
+ }
546
706
 
547
707
  export {
548
708
  runSetup,
549
709
  IMPORT_LINE
550
710
  };
551
- //# sourceMappingURL=chunk-XNTNXEWY.js.map
711
+ //# sourceMappingURL=chunk-GFUB57IT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/setup.ts","../src/commands/setup/install-path.ts","../src/commands/setup/next-steps.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport {\n copyFile,\n mkdir,\n readFile,\n writeFile,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { SpawnCliFn } from \"../agent/providers/claude/index.js\";\nimport {\n buildProviderModelChoices,\n buildProviderSetupView,\n getProviderLabel,\n parseAgentSelection,\n} from \"../agent/provider-view.js\";\nimport type {\n ProviderModelChoice,\n ProviderSetupView,\n} from \"../agent/provider-view.js\";\nimport {\n isAgentProviderId,\n readConfig,\n writeConfig,\n type AgentProviderId,\n} from \"../update/config.js\";\nimport { runHookInstall } from \"./hook.js\";\nimport {\n detectCurrentInstallPath,\n detectEphemeral,\n spawnGlobalInstall,\n} from \"./setup/install-path.js\";\nimport {\n countExistingPages,\n printNextSteps,\n} from \"./setup/next-steps.js\";\n\n/**\n * `codealmanac setup` — the MCP-style branded TUI that runs when a user\n * invokes the bare `codealmanac` binary (or `almanac setup` / `codealmanac\n * setup` explicitly).\n *\n * Model: `mcp-ts/src/setup.ts` from openalmanac. Same ASCII banner + badge\n * + step-indicator style, same interactive + `--yes` + non-interactive\n * modes.\n *\n * Three things get installed:\n *\n * 1. The `SessionEnd` hook in `~/.claude/settings.json` (delegated to\n * `runHookInstall` from `./hook.ts`).\n * 2. Provider-specific agent guidance for the chosen default provider.\n * Claude uses `~/.claude/CLAUDE.md`; Codex uses `~/.codex/AGENTS.md`;\n * Cursor uses a project rule in `.cursor/rules/`.\n *\n * Everything is idempotent — running setup again is safe. `--skip-hook`\n * and `--skip-guides` opt out of the individual installs. `--yes` or a\n * non-TTY stdin skips all prompts and installs everything.\n */\n\nexport interface SetupOptions {\n /** Install everything without prompting. */\n yes?: boolean;\n /** Don't install auto-capture hooks. */\n skipHook?: boolean;\n /** Don't install provider-specific guides. */\n skipGuides?: boolean;\n /** Set the default provider during setup. */\n agent?: string;\n /** Set the default model for the selected provider during setup. */\n model?: string;\n\n // ─── Injection points (tests only) ────────────────────────────────\n /** Override the subprocess spawner for `claude auth status`. */\n spawnCli?: SpawnCliFn;\n /** Override `~/.claude/settings.json` path. */\n settingsPath?: string;\n /** Override the bundled hook script path. */\n hookScriptPath?: string;\n /** Override the stable hooks directory for the hook script copy. */\n stableHooksDir?: string;\n /** Override `~/.claude/` dir for guide install. */\n claudeDir?: string;\n /** Override the directory containing `mini.md` / `reference.md`. */\n guidesDir?: string;\n /** Override interactivity; defaults to `process.stdin.isTTY`. */\n isTTY?: boolean;\n /** Stdout sink; defaults to `process.stdout`. */\n stdout?: NodeJS.WritableStream;\n /**\n * Override the install-path probe result. When `null` the probe is\n * bypassed (tests that don't care about the ephemeral-path step).\n * When a string it's treated as the detected install path.\n */\n installPath?: string | null;\n /**\n * Override the npm global install spawner (tests inject a no-op to\n * avoid actually spawning npm during CI).\n */\n spawnGlobalInstall?: () => Promise<void>;\n}\n\nexport interface SetupResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n// ─── ANSI helpers ────────────────────────────────────────────────────\n\nconst RST = \"\\x1b[0m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst ACCENT_BG = \"\\x1b[48;5;252m\\x1b[38;5;16m\";\n\nconst GRADIENT = [\n \"\\x1b[38;5;255m\",\n \"\\x1b[38;5;253m\",\n \"\\x1b[38;5;251m\",\n \"\\x1b[38;5;249m\",\n \"\\x1b[38;5;246m\",\n \"\\x1b[38;5;243m\",\n];\n\n// `codealmanac` 11-letter ASCII banner. Chosen for tasteful rendering —\n// same banner used in the MCP setup wizard design, retooled letters for\n// the word \"codealmanac\". Each glyph is 6 lines tall.\n//\n// If you tweak this, keep it to ≤80 visual columns wide so it fits in\n// narrow terminals (80 cols is the classic default).\nconst LOGO_LINES = [\n \" ___ ___ ___ ___ _ _ __ __ _ _ _ _ ___ \",\n \" / __/ _ \\\\| \\\\| __| /_\\\\ | | | \\\\/ | /_\\\\ | \\\\| | /_\\\\ / __|\",\n \"| (_| (_) | |) | _| / _ \\\\| |__| |\\\\/| |/ _ \\\\| .` |/ _ \\\\ (__ \",\n \" \\\\___\\\\___/|___/|___/_/ \\\\_\\\\____|_| |_/_/ \\\\_\\\\_|\\\\_/_/ \\\\_\\\\___|\",\n \" \",\n \" a living wiki for codebases, for your agent \",\n];\n\nconst BAR = ` ${DIM}\\u2502${RST}`;\n\nfunction printBanner(out: NodeJS.WritableStream): void {\n out.write(\"\\n\");\n for (let i = 0; i < LOGO_LINES.length; i++) {\n const color = GRADIENT[Math.min(i, GRADIENT.length - 1)] ?? \"\";\n out.write(`${color}${LOGO_LINES[i]}${RST}\\n`);\n }\n out.write(`\\n${WHITE_BOLD} Install provider integrations${RST}\\n`);\n}\n\nfunction printBadge(out: NodeJS.WritableStream): void {\n out.write(`\\n ${ACCENT_BG} codealmanac ${RST}\\n\\n`);\n}\n\nfunction stepDone(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c7${RST} ${msg}\\n`);\n}\n\nfunction stepActive(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c6${RST} ${msg}\\n`);\n}\n\nfunction stepSkipped(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${DIM}\\u25cb ${msg}${RST}\\n`);\n}\n\n// ─── Entry point ─────────────────────────────────────────────────────\n\nexport async function runSetup(\n options: SetupOptions = {},\n): Promise<SetupResult> {\n const out = options.stdout ?? process.stdout;\n const isTTY =\n options.isTTY ?? (process.stdin.isTTY === true);\n const interactive = isTTY && options.yes !== true;\n\n // No-op fast path. When the caller explicitly skipped every install\n // step, rendering the full banner + step markers + \"Setup complete\"\n // box is actively misleading — nothing was actually set up. Emit a\n // single terse line and exit so the user gets honest feedback and\n // piped callers (CI, scripts) don't parse through nine lines of ANSI\n // to conclude nothing happened.\n if (options.skipHook === true && options.skipGuides === true) {\n out.write(\n \"codealmanac: nothing to install — use --help to see what setup does\\n\",\n );\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n }\n\n printBanner(out);\n printBadge(out);\n\n const agentChoice = await chooseDefaultAgent({\n out,\n interactive,\n requested: options.agent,\n requestedModel: options.model,\n spawnCli: options.spawnCli,\n });\n if (!agentChoice.ok) {\n return {\n stdout: \"\",\n stderr: `almanac: ${agentChoice.error}\\n`,\n exitCode: 1,\n };\n }\n stepDone(\n out,\n `Default provider: ${WHITE_BOLD}${agentChoice.provider}${RST}` +\n ` (${agentChoice.model ?? \"provider default\"})`,\n );\n out.write(BAR + \"\\n\");\n\n // Step 1b: ephemeral install detection. When codealmanac was invoked via\n // `npx codealmanac` (no prior `npm i -g`), the binary lives inside an\n // npx cache directory or pnpm store that can be evicted at any time.\n // `almanac` is also not on PATH, so the user can't use it after setup.\n //\n // When we detect an ephemeral location, we offer (or, on --yes, perform)\n // a `npm install -g codealmanac` to make the install permanent.\n //\n // This is Bug #2 from codealmanac-known-bugs.md.\n const ephem = options.installPath !== undefined\n ? (options.installPath !== null\n ? detectEphemeral(options.installPath)\n : false)\n : detectEphemeral(detectCurrentInstallPath());\n if (ephem) {\n let globalAction: InstallDecision = \"install\";\n if (interactive) {\n globalAction = await confirm(\n out,\n `Running from an ephemeral npx location. Install globally so 'almanac' stays on PATH?`,\n true,\n );\n }\n if (globalAction === \"install\") {\n stepActive(out, \"Installing codealmanac globally…\");\n try {\n await (options.spawnGlobalInstall ?? spawnGlobalInstall)();\n stepDone(out, \"codealmanac installed globally (almanac now on PATH)\");\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n stepActive(out, `Global install failed: ${msg}`);\n out.write(\n ` ${DIM}You can retry manually: npm install -g codealmanac${RST}\\n`,\n );\n }\n } else {\n stepSkipped(\n out,\n `Global install ${DIM}skipped — almanac will not be on PATH after this session${RST}`,\n );\n }\n out.write(BAR + \"\\n\");\n }\n\n // Step 2: install the hook (default yes).\n let hookAction: InstallDecision = \"install\";\n if (options.skipHook === true) {\n hookAction = \"skip\";\n } else if (interactive) {\n hookAction = await confirm(\n out,\n \"Install auto-capture hooks for supported agents?\",\n true,\n );\n }\n\n let hookResultLine = \"\";\n if (hookAction === \"install\") {\n const res = await runHookInstall({\n source: \"all\",\n settingsPath: options.settingsPath,\n hookScriptPath: options.hookScriptPath,\n stableHooksDir: options.stableHooksDir,\n });\n if (res.exitCode !== 0) {\n stepActive(out, `Auto-capture hook: ${res.stderr.trim()}`);\n return {\n stdout: \"\",\n stderr: res.stderr,\n exitCode: res.exitCode,\n };\n }\n hookResultLine = res.stdout.includes(\"already installed\")\n ? `Auto-capture hooks ${DIM}already installed${RST}`\n : `Auto-capture hooks installed`;\n stepDone(out, hookResultLine);\n } else {\n stepSkipped(out, `Auto-capture hooks ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n // Step 3: install the provider-specific guide.\n let guidesAction: InstallDecision = \"install\";\n const providerLabel = getProviderLabel(agentChoice.provider);\n if (options.skipGuides === true) {\n guidesAction = \"skip\";\n } else if (interactive) {\n guidesAction = await confirm(\n out,\n `Install the codealmanac guide for ${providerLabel}?`,\n true,\n );\n }\n\n let guidesSummary: string;\n if (guidesAction === \"install\") {\n try {\n const summary = await installGuides({\n provider: agentChoice.provider,\n claudeDir: options.claudeDir ?? path.join(homedir(), \".claude\"),\n guidesDir: options.guidesDir ?? resolveGuidesDir(),\n cwd: process.cwd(),\n });\n guidesSummary = summary.anyChanges\n ? `Guide installed for ${summary.providerLabel} (${summary.filesWritten.join(\", \")})`\n : `Guide for ${summary.providerLabel} ${DIM}already installed${RST}`;\n stepDone(out, guidesSummary);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n stdout: \"\",\n stderr: `almanac: guide install failed: ${msg}\\n`,\n exitCode: 1,\n };\n }\n } else {\n stepSkipped(out, `Guides ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n stepDone(out, `${BLUE}Setup complete${RST}`);\n out.write(\"\\n\");\n\n // Detect whether the current working directory is inside a repo that\n // already has a wiki with pages. This fixes Bug #6 from\n // codealmanac-known-bugs.md: Engineer B clones a repo that already has\n // `.almanac/pages/` (committed by Engineer A) and gets told to run\n // `almanac bootstrap`, which is wrong — the wiki already exists.\n const existingPageCount = countExistingPages(process.cwd());\n printNextSteps(out, existingPageCount);\n printProviderNextSteps(out);\n\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n}\n\ntype AgentChoice =\n | { ok: true; provider: AgentProviderId; model: string | null }\n | { ok: false; error: string };\n\nasync function chooseDefaultAgent(args: {\n out: NodeJS.WritableStream;\n interactive: boolean;\n requested?: string;\n requestedModel?: string;\n spawnCli?: SpawnCliFn;\n}): Promise<AgentChoice> {\n const config = await readConfig();\n let view: ProviderSetupView | null = null;\n let selected = args.requested ?? config.agent.default;\n if (args.interactive || args.requested !== undefined) {\n view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });\n }\n if (args.interactive && args.requested === undefined && view !== null) {\n args.out.write(\" Choose default provider for bootstrap/capture:\\n\");\n view.choices.forEach((choice, index) => {\n const tag = choice.recommended ? \" recommended\" : \"\";\n const status = choice.ready ? \"ready\" : \"not ready\";\n const detail = choice.account ?? choice.fixCommand ?? choice.detail;\n args.out.write(\n ` ${index + 1}. ${choice.label.padEnd(6)} ${status.padEnd(9)}${tag} ${detail}\\n`,\n );\n });\n selected = (await promptText(\n args.out,\n \"Default provider\",\n view.recommendedProvider,\n )).toLowerCase();\n const number = Number.parseInt(selected, 10);\n if (\n Number.isInteger(number) &&\n number >= 1 &&\n number <= view.choices.length\n ) {\n selected = view.choices[number - 1]?.id ?? selected;\n }\n }\n const parsed = parseAgentSelection(selected);\n if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {\n return {\n ok: false,\n error:\n `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`,\n };\n }\n const provider = parsed.provider;\n let selectedChoice = view?.choices.find((choice) => choice.id === provider);\n if (\n args.interactive &&\n selectedChoice !== undefined &&\n !selectedChoice.ready &&\n selectedChoice.fixCommand?.startsWith(\"run: \") === true\n ) {\n const command = selectedChoice.fixCommand.slice(\"run: \".length);\n const runLogin = await confirm(\n args.out,\n `${selectedChoice.label} is not ready. Run '${command}' now?`,\n true,\n );\n if (runLogin === \"install\") {\n const login = await runLoginCommand(command);\n if (login.ok) {\n view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });\n selectedChoice = view.choices.find((choice) => choice.id === provider);\n } else {\n stepActive(args.out, `${selectedChoice.label} login failed: ${login.error}`);\n }\n }\n }\n const requestedModel = args.requestedModel ?? parsed.model;\n const model = requestedModel ?? await chooseProviderModel({\n out: args.out,\n interactive: args.interactive,\n provider,\n choice: selectedChoice,\n configuredModel: config.agent.models[provider] ?? null,\n });\n await writeConfig({\n ...config,\n agent: {\n ...config.agent,\n default: provider,\n models: {\n ...config.agent.models,\n [provider]: model,\n },\n },\n });\n if (!args.interactive || args.requested !== undefined) {\n const detail = selectedChoice?.ready === true\n ? \"ready\"\n : selectedChoice?.fixCommand ?? selectedChoice?.detail ?? \"status unknown\";\n stepDone(args.out, `Agent readiness: ${detail}`);\n }\n return { ok: true, provider, model };\n}\n\nasync function chooseProviderModel(args: {\n out: NodeJS.WritableStream;\n interactive: boolean;\n provider: AgentProviderId;\n choice?: ProviderSetupView[\"choices\"][number];\n configuredModel: string | null;\n}): Promise<string | null> {\n const choices =\n args.choice?.modelChoices ??\n buildProviderModelChoices(args.provider, args.configuredModel);\n const recommended =\n choices.find((choice) => choice.recommended) ??\n choices.find((choice) => choice.source === \"provider-default\");\n if (!args.interactive) {\n return args.configuredModel ?? recommended?.value ?? null;\n }\n\n args.out.write(` Choose model for ${getProviderLabel(args.provider)}:\\n`);\n choices.forEach((choice, index) => {\n const marker = choice.recommended ? \" recommended\" : \"\";\n const current = choice.value === args.configuredModel ? \" current\" : \"\";\n args.out.write(\n ` ${index + 1}. ${choice.label}${marker}${current}\\n`,\n );\n });\n const currentIndex = choices.findIndex((choice) =>\n choice.value === args.configuredModel\n );\n const recommendedIndex = choices.findIndex((choice) => choice.recommended);\n const defaultIndex =\n currentIndex >= 0\n ? currentIndex + 1\n : recommendedIndex >= 0\n ? recommendedIndex + 1\n : 1;\n const selected = await promptText(args.out, \"Model\", String(defaultIndex));\n const number = Number.parseInt(selected, 10);\n let modelChoice: ProviderModelChoice | undefined;\n if (\n Number.isInteger(number) &&\n number >= 1 &&\n number <= choices.length\n ) {\n modelChoice = choices[number - 1];\n } else {\n modelChoice = choices.find((choice) => choice.value === selected);\n }\n if (modelChoice?.source === \"custom\") {\n const custom = await promptText(args.out, \"Custom model id\", \"\");\n return custom.length > 0 ? custom : recommended?.value ?? null;\n }\n return modelChoice?.value ?? recommended?.value ?? null;\n}\n\nasync function runLoginCommand(command: string): Promise<\n | { ok: true }\n | { ok: false; error: string }\n> {\n return new Promise((resolve) => {\n const child = spawn(command, {\n shell: true,\n stdio: \"inherit\",\n });\n child.on(\"error\", (err) => {\n resolve({ ok: false, error: err.message });\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ ok: true });\n return;\n }\n resolve({ ok: false, error: `exited ${code ?? 1}` });\n });\n });\n}\n\n// ─── Guide installation ──────────────────────────────────────────────\n\ninterface InstallGuidesOptions {\n provider: AgentProviderId;\n claudeDir: string;\n guidesDir: string;\n cwd: string;\n}\n\ninterface InstallGuidesResult {\n providerLabel: string;\n anyChanges: boolean;\n filesWritten: string[];\n}\n\nconst CODEX_BLOCK_START = \"<!-- codealmanac:start -->\";\nconst CODEX_BLOCK_END = \"<!-- codealmanac:end -->\";\n\n/**\n * Install provider-specific usage guidance. Each provider gets the same\n * core instructions, but in the place that provider actually reads.\n *\n * Returns a summary the caller uses to decide whether to say \"installed\"\n * or \"already installed\" in the TUI.\n */\nasync function installGuides(\n options: InstallGuidesOptions,\n): Promise<InstallGuidesResult> {\n const srcMini = path.join(options.guidesDir, \"mini.md\");\n const srcRef = path.join(options.guidesDir, \"reference.md\");\n if (!existsSync(srcMini)) {\n throw new Error(`missing bundled guide: ${srcMini}`);\n }\n if (!existsSync(srcRef)) {\n throw new Error(`missing bundled guide: ${srcRef}`);\n }\n\n switch (options.provider) {\n case \"claude\":\n return await installClaudeGuides(options, srcMini, srcRef);\n case \"codex\":\n return await installCodexGuides(srcMini, srcRef);\n case \"cursor\":\n return await installCursorGuides(options.cwd, srcMini);\n }\n}\n\nasync function installClaudeGuides(\n options: InstallGuidesOptions,\n srcMini: string,\n srcRef: string,\n): Promise<InstallGuidesResult> {\n await mkdir(options.claudeDir, { recursive: true });\n\n const destMini = path.join(options.claudeDir, \"codealmanac.md\");\n const destRef = path.join(options.claudeDir, \"codealmanac-reference.md\");\n\n const miniChanged = await copyIfChanged(srcMini, destMini);\n const refChanged = await copyIfChanged(srcRef, destRef);\n\n const claudeMd = path.join(options.claudeDir, \"CLAUDE.md\");\n const importChanged = await ensureImport(claudeMd);\n\n const filesWritten: string[] = [];\n if (miniChanged) filesWritten.push(\"codealmanac.md\");\n if (refChanged) filesWritten.push(\"codealmanac-reference.md\");\n if (importChanged) filesWritten.push(\"CLAUDE.md\");\n\n return {\n providerLabel: \"Claude\",\n anyChanges: filesWritten.length > 0,\n filesWritten,\n };\n}\n\nasync function installCodexGuides(\n srcMini: string,\n srcRef: string,\n): Promise<InstallGuidesResult> {\n const codexDir = path.join(homedir(), \".codex\");\n await mkdir(codexDir, { recursive: true });\n const destMini = path.join(codexDir, \"codealmanac.md\");\n const destRef = path.join(codexDir, \"codealmanac-reference.md\");\n const miniChanged = await copyIfChanged(srcMini, destMini);\n const refChanged = await copyIfChanged(srcRef, destRef);\n const agentsChanged = await ensureManagedBlock(\n path.join(codexDir, \"AGENTS.md\"),\n codexGuideBlock(),\n );\n\n const filesWritten: string[] = [];\n if (miniChanged) filesWritten.push(\"codealmanac.md\");\n if (refChanged) filesWritten.push(\"codealmanac-reference.md\");\n if (agentsChanged) filesWritten.push(\"AGENTS.md\");\n\n return {\n providerLabel: \"Codex\",\n anyChanges: filesWritten.length > 0,\n filesWritten,\n };\n}\n\nasync function installCursorGuides(\n cwd: string,\n srcMini: string,\n): Promise<InstallGuidesResult> {\n const rulesDir = path.join(cwd, \".cursor\", \"rules\");\n await mkdir(rulesDir, { recursive: true });\n const mini = await readFile(srcMini, \"utf8\");\n const dest = path.join(rulesDir, \"codealmanac.mdc\");\n const body =\n \"---\\n\" +\n \"description: Use codealmanac wiki context during coding work\\n\" +\n \"alwaysApply: true\\n\" +\n \"---\\n\\n\" +\n mini;\n const changed = await writeIfChanged(dest, body);\n\n return {\n providerLabel: \"Cursor\",\n anyChanges: changed,\n filesWritten: changed ? [\".cursor/rules/codealmanac.mdc\"] : [],\n };\n}\n\nasync function copyIfChanged(src: string, dest: string): Promise<boolean> {\n const srcBytes = await readFile(src);\n if (existsSync(dest)) {\n try {\n const destBytes = await readFile(dest);\n if (srcBytes.equals(destBytes)) return false;\n } catch {\n // Fall through to write.\n }\n }\n await copyFile(src, dest);\n return true;\n}\n\nasync function writeIfChanged(dest: string, body: string): Promise<boolean> {\n if (existsSync(dest)) {\n try {\n if (await readFile(dest, \"utf8\") === body) return false;\n } catch {\n // Fall through to write.\n }\n }\n await writeFile(dest, body, \"utf8\");\n return true;\n}\n\nasync function ensureManagedBlock(\n file: string,\n block: string,\n): Promise<boolean> {\n let existing = \"\";\n if (existsSync(file)) existing = await readFile(file, \"utf8\");\n const pattern = new RegExp(\n `${escapeRegex(CODEX_BLOCK_START)}[\\\\s\\\\S]*?${escapeRegex(CODEX_BLOCK_END)}`,\n );\n const next = pattern.test(existing)\n ? existing.replace(pattern, block.trimEnd())\n : `${existing.trimEnd()}${existing.trim().length > 0 ? \"\\n\\n\" : \"\"}${block.trimEnd()}\\n`;\n const normalized = next.endsWith(\"\\n\") ? next : `${next}\\n`;\n if (normalized === existing) return false;\n await writeFile(file, normalized, \"utf8\");\n return true;\n}\n\nfunction codexGuideBlock(): string {\n return [\n CODEX_BLOCK_START,\n \"# codealmanac\",\n \"\",\n \"This machine has codealmanac installed: a local wiki for codebases.\",\n \"Before codealmanac/wiki-related work, read `~/.codex/codealmanac.md`.\",\n \"For the full command reference, read `~/.codex/codealmanac-reference.md` on demand.\",\n CODEX_BLOCK_END,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** The exact import line we manage. Changing this requires updating\n * uninstall too. */\nexport const IMPORT_LINE = \"@~/.claude/codealmanac.md\";\n\n/**\n * Append the import line to `~/.claude/CLAUDE.md` if it isn't already\n * present. Creates the file if absent. Returns true when we wrote, false\n * when the line was already there.\n *\n * We match on `@~/.claude/codealmanac.md` appearing on any non-empty\n * line (trimmed). This catches both the bare line we write and any\n * user-edited variant (comments, trailing whitespace). We deliberately\n * do NOT try to repair a user who deleted the newline — that's their\n * file to shape.\n */\nasync function ensureImport(claudeMdPath: string): Promise<boolean> {\n let existing = \"\";\n if (existsSync(claudeMdPath)) {\n existing = await readFile(claudeMdPath, \"utf8\");\n }\n if (hasImportLine(existing)) return false;\n\n const sep =\n existing.length === 0 ? \"\" : existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n const body = `${existing}${sep}${IMPORT_LINE}\\n`;\n await writeFile(claudeMdPath, body, \"utf8\");\n return true;\n}\n\nexport function hasImportLine(contents: string): boolean {\n // Match line-starts-with-token rather than exact-line equality so a\n // user who annotated the import line (`@~/.claude/codealmanac.md #\n // codealmanac`) doesn't cause us to re-append a duplicate below.\n // The trailing-character check rules out accidental matches on a\n // longer line like `@~/.claude/codealmanac.md-extra`.\n const lines = contents.split(/\\r?\\n/).map((l) => l.trim());\n return lines.some((line) => {\n if (line === IMPORT_LINE) return true;\n if (!line.startsWith(IMPORT_LINE)) return false;\n const next = line[IMPORT_LINE.length];\n return next === \" \" || next === \"\\t\";\n });\n}\n\n// ─── Interactive prompt ──────────────────────────────────────────────\n\ntype InstallDecision = \"install\" | \"skip\";\n\n/**\n * Minimal `[Y/n]` prompt. No raw mode, no cursor — just readline. The\n * MCP setup uses a fancy arrow-key TUI for multi-choice; we only have\n * binary decisions here, so a line-reader prompt is clearer and doesn't\n * fight with the step-indicator rendering above it.\n */\nfunction confirm(\n out: NodeJS.WritableStream,\n question: string,\n defaultYes: boolean,\n): Promise<InstallDecision> {\n return new Promise((resolve) => {\n const hint = defaultYes ? \"[Y/n]\" : \"[y/N]\";\n out.write(` ${BLUE}\\u25c6${RST} ${question} ${DIM}${hint}${RST} `);\n\n let buf = \"\";\n const onData = (chunk: Buffer): void => {\n buf += chunk.toString(\"utf8\");\n const nl = buf.indexOf(\"\\n\");\n if (nl === -1) return;\n process.stdin.removeListener(\"data\", onData);\n process.stdin.pause();\n\n const answer = buf.slice(0, nl).trim().toLowerCase();\n const accepted =\n answer.length === 0\n ? defaultYes\n : answer === \"y\" || answer === \"yes\";\n resolve(accepted ? \"install\" : \"skip\");\n };\n\n process.stdin.resume();\n process.stdin.on(\"data\", onData);\n });\n}\n\nfunction promptText(\n out: NodeJS.WritableStream,\n question: string,\n defaultValue: string,\n): Promise<string> {\n return new Promise((resolve) => {\n out.write(\n ` ${BLUE}\\u25c6${RST} ${question} ${DIM}[${defaultValue}]${RST} `,\n );\n\n let buf = \"\";\n const onData = (chunk: Buffer): void => {\n buf += chunk.toString(\"utf8\");\n const nl = buf.indexOf(\"\\n\");\n if (nl === -1) return;\n process.stdin.removeListener(\"data\", onData);\n process.stdin.pause();\n\n const answer = buf.slice(0, nl).trim();\n resolve(answer.length === 0 ? defaultValue : answer);\n };\n\n process.stdin.resume();\n process.stdin.on(\"data\", onData);\n });\n}\n\n// ─── Guides path resolution ──────────────────────────────────────────\n\n/**\n * Locate `guides/` relative to the installed package. Mirrors\n * `resolvePromptsDir` from `src/agent/prompts.ts`.\n *\n * Two runtime layouts to handle:\n *\n * 1. **Bundled dist.** `dist/codealmanac.js` → walk one level up →\n * `guides/`.\n * 2. **Source (tests / tsx).** `src/commands/setup.ts` → walk two\n * levels up → `guides/`.\n *\n * We also try `createRequire` to resolve the package root from the\n * `codealmanac/package.json` manifest, as a belt-and-suspenders fallback\n * for unusual install layouts (monorepo hoisting, etc.). That path is\n * only exercised when the direct walk-up fails.\n */\nexport function resolveGuidesDir(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"..\", \"guides\"), // dist layout\n path.resolve(here, \"..\", \"..\", \"guides\"), // src layout\n path.resolve(here, \"..\", \"..\", \"..\", \"guides\"),\n ];\n for (const dir of candidates) {\n if (looksLikeGuidesDir(dir)) return dir;\n }\n // Fallback: resolve via the package.json of the currently-running\n // codealmanac. createRequire lets us ask Node's resolver rather than\n // guessing at directory layouts.\n try {\n const require = createRequire(import.meta.url);\n const pkgJson = require.resolve(\"codealmanac/package.json\");\n const guides = path.join(path.dirname(pkgJson), \"guides\");\n if (looksLikeGuidesDir(guides)) return guides;\n } catch {\n // Ignore — we'll throw with the candidate list below.\n }\n throw new Error(\n \"could not locate bundled guides/ directory. Tried:\\n\" +\n candidates.map((c) => ` - ${c}`).join(\"\\n\"),\n );\n}\n\nfunction looksLikeGuidesDir(dir: string): boolean {\n return existsSync(path.join(dir, \"mini.md\"));\n}\n\nfunction printProviderNextSteps(out: NodeJS.WritableStream): void {\n out.write(` ${DIM}Change provider/model later:${RST}\\n`);\n out.write(` ${DIM} almanac agents list${RST}\\n`);\n out.write(` ${DIM} almanac agents use <claude|codex|cursor>${RST}\\n`);\n out.write(\n ` ${DIM} almanac agents model <provider> <model|--default>${RST}\\n\\n`,\n );\n}\n","import { execFile } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Return the directory of the currently-running codealmanac install by\n * walking up from this module's file to the nearest `package.json` whose\n * `name` is `codealmanac`. Returns the empty string when the walk fails.\n */\nexport function detectCurrentInstallPath(): string {\n try {\n const req = createRequire(import.meta.url);\n const here = fileURLToPath(import.meta.url);\n let dir = path.dirname(here);\n for (let i = 0; i < 6; i++) {\n const pkgPath = path.join(dir, \"package.json\");\n try {\n const raw = req(\"fs\").readFileSync(pkgPath, \"utf-8\") as string;\n const pkg = JSON.parse(raw) as { name?: unknown };\n if (pkg.name === \"codealmanac\") return dir;\n } catch {\n // keep walking\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // import.meta.url unavailable — not ephemeral for our purposes.\n }\n return \"\";\n}\n\n/**\n * Return true when the given install path looks ephemeral.\n *\n * Ephemeral locations we recognize:\n * - `~/.npm/_npx/` — npm's npx cache (GC'd on version bumps or\n * `npm cache clean`)\n * - `~/.local/share/pnpm/dlx/` — pnpm's dlx (like npx) cache\n * - `/tmp/` or `/var/folders/` — common CI / temp paths\n *\n * A global install (`~/.nvm/.../lib/node_modules/`, `/usr/local/lib/...`,\n * `~/.local/lib/node_modules/`) is NOT ephemeral.\n */\nexport function detectEphemeral(installPath: string): boolean {\n if (installPath.length === 0) return false;\n const home = homedir();\n if (installPath.startsWith(path.join(home, \".npm\", \"_npx\"))) return true;\n if (\n installPath.startsWith(path.join(home, \".local\", \"share\", \"pnpm\", \"dlx\"))\n ) return true;\n if (installPath.startsWith(\"/tmp/\")) return true;\n if (installPath.startsWith(\"/var/folders/\")) return true;\n return false;\n}\n\n/**\n * Spawn `npm install -g codealmanac@latest` in a child process and wait\n * for it to finish. Rejects on non-zero exit or spawn error.\n */\nexport function spawnGlobalInstall(): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(\n \"npm\",\n [\"install\", \"-g\", \"codealmanac@latest\"],\n { shell: false },\n (err, _stdout, stderr) => {\n if (err !== null) {\n reject(\n new Error(\n stderr.length > 0\n ? stderr.trim().split(\"\\n\")[0] ?? err.message\n : err.message,\n ),\n );\n } else {\n resolve();\n }\n },\n );\n });\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nconst RST = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst BLUE_DIM = \"\\x1b[38;5;69m\";\n\n/**\n * Print the \"Next steps\" box. When `existingPageCount` is greater than 0,\n * the current working directory already has a wiki with committed pages.\n * In that case we skip the `almanac bootstrap` step and tell the user to\n * start querying.\n */\nexport function printNextSteps(\n out: NodeJS.WritableStream,\n existingPageCount: number,\n): void {\n const innerW = 62;\n const vis = (s: string): number =>\n s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n const row = (content: string): string => {\n const padding = Math.max(0, innerW - vis(content));\n return ` ${BLUE_DIM}\\u2502${RST}${content}${\" \".repeat(padding)}${BLUE_DIM}\\u2502${RST}\\n`;\n };\n const empty = row(\"\");\n\n out.write(` ${BLUE_DIM}\\u256d${\"─\".repeat(innerW)}\\u256e${RST}\\n`);\n out.write(empty);\n out.write(row(` ${WHITE_BOLD}Next steps${RST}`));\n out.write(empty);\n\n if (existingPageCount > 0) {\n out.write(\n row(\n ` ${BLUE}\\u25c7${RST} This repo already has a wiki ${DIM}(${existingPageCount} page${existingPageCount === 1 ? \"\" : \"s\"})${RST}`,\n ),\n );\n out.write(empty);\n out.write(row(` ${BLUE}1.${RST} Start querying your wiki:`));\n out.write(row(` ${BOLD}almanac search --mentions <file>${RST}`));\n out.write(\n row(` ${BLUE}2.${RST} Work normally — capture runs on session end`),\n );\n } else {\n out.write(\n row(` ${BLUE}1.${RST} ${BOLD}cd${RST} into a repo you want to document`),\n );\n out.write(\n row(\n ` ${BLUE}2.${RST} ${BOLD}almanac bootstrap${RST} ${DIM}# scaffold the wiki${RST}`,\n ),\n );\n out.write(\n row(` ${BLUE}3.${RST} Work normally — capture runs on session end`),\n );\n }\n\n out.write(empty);\n out.write(` ${BLUE_DIM}\\u2570${\"─\".repeat(innerW)}\\u256f${RST}\\n\\n`);\n}\n\n/**\n * Count `.md` files in `.almanac/pages/` under the current working\n * directory or any parent. Returns 0 when no wiki is found or the pages\n * directory is empty.\n */\nexport function countExistingPages(cwd: string): number {\n try {\n let dir = cwd;\n for (let i = 0; i < 10; i++) {\n const pagesDir = path.join(dir, \".almanac\", \"pages\");\n if (existsSync(pagesDir)) {\n try {\n const entries = readdirSync(pagesDir);\n return entries.filter((e) => e.endsWith(\".md\")).length;\n } catch {\n return 0;\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // Swallow — never crash setup because of this.\n }\n return 0;\n}\n"],"mappings":"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","names":["existsSync","createRequire","homedir","path","fileURLToPath","path","RST","DIM","WHITE_BOLD","BLUE","path","homedir","existsSync","fileURLToPath","require","createRequire"]}
@@ -6,6 +6,16 @@ function emit(result) {
6
6
  if (result.stdout.length > 0) process.stdout.write(result.stdout);
7
7
  if (result.exitCode !== 0) process.exitCode = result.exitCode;
8
8
  }
9
+ function withWarning(result, warning) {
10
+ return {
11
+ ...result,
12
+ stderr: `${warning}${result.stderr}`
13
+ };
14
+ }
15
+ function deprecationWarning(oldUsage, newUsage) {
16
+ return `almanac: warning: \`${oldUsage}\` is deprecated; use \`${newUsage}\`.
17
+ `;
18
+ }
9
19
  function collectOption(value, previous) {
10
20
  return [...previous, value];
11
21
  }
@@ -27,8 +37,10 @@ async function readStdin() {
27
37
 
28
38
  export {
29
39
  emit,
40
+ withWarning,
41
+ deprecationWarning,
30
42
  collectOption,
31
43
  parsePositiveInt,
32
44
  readStdin
33
45
  };
34
- //# sourceMappingURL=chunk-P3LDTCLB.js.map
46
+ //# sourceMappingURL=chunk-H37GKBWI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/helpers.ts"],"sourcesContent":["export interface CommandResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport function emit(result: CommandResult): void {\n if (result.stderr.length > 0) process.stderr.write(result.stderr);\n if (result.stdout.length > 0) process.stdout.write(result.stdout);\n if (result.exitCode !== 0) process.exitCode = result.exitCode;\n}\n\nexport function withWarning(\n result: CommandResult,\n warning: string,\n): CommandResult {\n return {\n ...result,\n stderr: `${warning}${result.stderr}`,\n };\n}\n\nexport function deprecationWarning(oldUsage: string, newUsage: string): string {\n return `almanac: warning: \\`${oldUsage}\\` is deprecated; use \\`${newUsage}\\`.\\n`;\n}\n\nexport function collectOption(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\nexport function parsePositiveInt(value: string): number {\n const n = Number.parseInt(value, 10);\n if (!Number.isFinite(n) || n < 0) {\n throw new Error(`invalid --limit \"${value}\" (expected a non-negative integer)`);\n }\n return n;\n}\n\nexport async function readStdin(): Promise<string> {\n if (process.stdin.isTTY === true) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n"],"mappings":";;;AAMO,SAAS,KAAK,QAA6B;AAChD,MAAI,OAAO,OAAO,SAAS,EAAG,SAAQ,OAAO,MAAM,OAAO,MAAM;AAChE,MAAI,OAAO,OAAO,SAAS,EAAG,SAAQ,OAAO,MAAM,OAAO,MAAM;AAChE,MAAI,OAAO,aAAa,EAAG,SAAQ,WAAW,OAAO;AACvD;AAEO,SAAS,YACd,QACA,SACe;AACf,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,GAAG,OAAO,GAAG,OAAO,MAAM;AAAA,EACpC;AACF;AAEO,SAAS,mBAAmB,UAAkB,UAA0B;AAC7E,SAAO,uBAAuB,QAAQ,2BAA2B,QAAQ;AAAA;AAC3E;AAEO,SAAS,cAAc,OAAe,UAA8B;AACzE,SAAO,CAAC,GAAG,UAAU,KAAK;AAC5B;AAEO,SAAS,iBAAiB,OAAuB;AACtD,QAAM,IAAI,OAAO,SAAS,OAAO,EAAE;AACnC,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,GAAG;AAChC,UAAM,IAAI,MAAM,oBAAoB,KAAK,qCAAqC;AAAA,EAChF;AACA,SAAO;AACT;AAEA,eAAsB,YAA6B;AACjD,MAAI,QAAQ,MAAM,UAAU,KAAM,QAAO;AACzC,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,EACjE;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC9C;","names":[]}