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