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.
- package/README.md +27 -14
- package/dist/agents-RVTQYE6A.js +25 -0
- package/dist/chunk-6BJUYZ43.js +195 -0
- package/dist/chunk-6BJUYZ43.js.map +1 -0
- package/dist/chunk-BGUID5BS.js +766 -0
- package/dist/chunk-BGUID5BS.js.map +1 -0
- package/dist/{chunk-NBVIEZZQ.js → chunk-DL5BXZCX.js} +53 -3
- package/dist/chunk-DL5BXZCX.js.map +1 -0
- package/dist/{chunk-XNTNXEWY.js → chunk-GFUB57IT.js} +243 -83
- package/dist/chunk-GFUB57IT.js.map +1 -0
- package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
- package/dist/chunk-H37GKBWI.js.map +1 -0
- package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
- package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
- package/dist/chunk-P5WGG4FJ.js +359 -0
- package/dist/chunk-P5WGG4FJ.js.map +1 -0
- package/dist/{chunk-HNVOYWC2.js → chunk-SMIK2YLU.js} +165 -76
- package/dist/chunk-SMIK2YLU.js.map +1 -0
- package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
- package/dist/chunk-TILAKDN6.js.map +1 -0
- package/dist/chunk-TT6ZP4GS.js +282 -0
- package/dist/chunk-TT6ZP4GS.js.map +1 -0
- package/dist/{cli-6BOB6KAN.js → cli-CL4ID7EO.js} +123 -33
- package/dist/cli-CL4ID7EO.js.map +1 -0
- package/dist/codealmanac.js +1 -1
- package/dist/config-ML2RCR7J.js +16 -0
- package/dist/doctor-DOLJRGS4.js +17 -0
- package/dist/{register-commands-IXYE5CNZ.js → register-commands-FBJ6XQ3L.js} +296 -398
- package/dist/register-commands-FBJ6XQ3L.js.map +1 -0
- package/dist/uninstall-DX6LFKMX.js +15 -0
- package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
- package/guides/mini.md +4 -4
- package/guides/reference.md +75 -16
- package/package.json +1 -1
- package/dist/agents-RVYQ44DB.js +0 -16
- package/dist/auth-S5DVUIUJ.js +0 -18
- package/dist/chunk-HNVOYWC2.js.map +0 -1
- package/dist/chunk-NBVIEZZQ.js.map +0 -1
- package/dist/chunk-P3LDTCLB.js.map +0 -1
- package/dist/chunk-PIYJQE4Z.js +0 -102
- package/dist/chunk-PIYJQE4Z.js.map +0 -1
- package/dist/chunk-SSYMRT4I.js +0 -126
- package/dist/chunk-SSYMRT4I.js.map +0 -1
- package/dist/chunk-TWM7I2LU.js +0 -116
- package/dist/chunk-TWM7I2LU.js.map +0 -1
- package/dist/chunk-V3QOQSXI.js.map +0 -1
- package/dist/chunk-WRUSDYYE.js +0 -97
- package/dist/chunk-WRUSDYYE.js.map +0 -1
- package/dist/chunk-XNTNXEWY.js.map +0 -1
- package/dist/cli-6BOB6KAN.js.map +0 -1
- package/dist/doctor-DD7EQGCA.js +0 -18
- package/dist/register-commands-IXYE5CNZ.js.map +0 -1
- package/dist/uninstall-OBV4Z3JE.js +0 -16
- /package/dist/{agents-RVYQ44DB.js.map → agents-RVTQYE6A.js.map} +0 -0
- /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
- /package/dist/{doctor-DD7EQGCA.js.map → doctor-DOLJRGS4.js.map} +0 -0
- /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-DX6LFKMX.js.map} +0 -0
- /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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
235
|
-
|
|
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(
|
|
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
|
|
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
|
|
282
|
+
hookAction = await confirm(
|
|
285
283
|
out,
|
|
286
|
-
"Install auto-capture hooks for
|
|
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, `
|
|
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, `
|
|
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
|
|
315
|
+
guidesAction = await confirm(
|
|
317
316
|
out,
|
|
318
|
-
|
|
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 ? `
|
|
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
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
|
|
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
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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:
|
|
418
|
+
default: provider,
|
|
419
|
+
models: {
|
|
420
|
+
...config.agent.models,
|
|
421
|
+
[provider]: model
|
|
422
|
+
}
|
|
400
423
|
}
|
|
401
424
|
});
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
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 {
|
|
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()
|
|
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-
|
|
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-
|
|
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":[]}
|