@cruxy/cli 0.3.0 → 0.6.0
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 +46 -1
- package/dist/agent/index.d.ts +0 -1
- package/dist/agent/index.js +0 -1
- package/dist/agent/loop.js +2 -2
- package/dist/agent/prompts.d.ts +0 -2
- package/dist/agent/prompts.js +3 -3
- package/dist/approval/classify.d.ts +18 -0
- package/dist/approval/classify.js +162 -0
- package/dist/approval/index.d.ts +5 -0
- package/dist/approval/index.js +5 -0
- package/dist/approval/policy.d.ts +37 -0
- package/dist/approval/policy.js +81 -0
- package/dist/approval/prompt.d.ts +33 -0
- package/dist/approval/prompt.js +212 -0
- package/dist/approval/service.d.ts +36 -0
- package/dist/approval/service.js +37 -0
- package/dist/approval/types.d.ts +64 -0
- package/dist/approval/types.js +1 -0
- package/dist/cli/commands/config.js +2 -3
- package/dist/cli/commands/index.js +5 -2
- package/dist/cli/commands/pr.d.ts +8 -0
- package/dist/cli/commands/pr.js +87 -0
- package/dist/cli/commands/run.js +31 -23
- package/dist/cli/program.js +26 -4
- package/dist/cli/repl.js +15 -2
- package/dist/config/manager.js +5 -4
- package/dist/config/schema.d.ts +38 -9
- package/dist/config/schema.js +13 -4
- package/dist/errors/boundary.d.ts +43 -0
- package/dist/errors/boundary.js +73 -0
- package/dist/errors/constructors.d.ts +52 -0
- package/dist/errors/constructors.js +329 -0
- package/dist/errors/format.d.ts +31 -0
- package/dist/errors/format.js +60 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/types.d.ts +79 -0
- package/dist/errors/types.js +118 -0
- package/dist/index.js +8 -5
- package/dist/indexing/embedder.js +3 -3
- package/dist/indexing/service.js +4 -1
- package/dist/skills/loader.d.ts +2 -1
- package/dist/skills/loader.js +0 -0
- package/dist/skills/parser.d.ts +6 -4
- package/dist/skills/parser.js +13 -5
- package/dist/tools/create-pull-request.d.ts +24 -0
- package/dist/tools/create-pull-request.js +83 -0
- package/dist/tools/file/apply-patch.js +3 -3
- package/dist/tools/file/edit-file.js +6 -3
- package/dist/tools/file/paths.d.ts +4 -2
- package/dist/tools/file/paths.js +5 -3
- package/dist/tools/file/write-file.js +6 -3
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/registry.js +2 -0
- package/dist/tools/shell/run-command.js +11 -3
- package/dist/tools/types.d.ts +25 -6
- package/dist/vcs/auth.d.ts +22 -0
- package/dist/vcs/auth.js +29 -0
- package/dist/vcs/generate.d.ts +72 -0
- package/dist/vcs/generate.js +265 -0
- package/dist/vcs/git.d.ts +52 -0
- package/dist/vcs/git.js +152 -0
- package/dist/vcs/github.d.ts +44 -0
- package/dist/vcs/github.js +145 -0
- package/dist/vcs/guidance.d.ts +20 -0
- package/dist/vcs/guidance.js +76 -0
- package/dist/vcs/index.d.ts +7 -0
- package/dist/vcs/index.js +7 -0
- package/dist/vcs/service.d.ts +53 -0
- package/dist/vcs/service.js +79 -0
- package/dist/vcs/types.d.ts +57 -0
- package/dist/vcs/types.js +6 -0
- package/package.json +1 -1
- package/dist/agent/approval.d.ts +0 -41
- package/dist/agent/approval.js +0 -179
package/dist/cli/repl.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import readline from "node:readline";
|
|
2
2
|
import pc from "picocolors";
|
|
3
|
+
import { formatError, fromUnknown, isVerbose, shouldUseColor, } from "../errors/index.js";
|
|
3
4
|
import { logger } from "../utils/logger.js";
|
|
4
5
|
import { createStreamPrinter } from "./stream-print.js";
|
|
5
6
|
const PROMPT = `${pc.cyan("cruxy")} ${pc.dim("›")} `;
|
|
@@ -17,7 +18,7 @@ const defaultIO = () => ({
|
|
|
17
18
|
/**
|
|
18
19
|
* Read one line using a readline interface that is created and then **closed
|
|
19
20
|
* before this resolves**. This is the crux of the stdin coordination: while a
|
|
20
|
-
* turn runs (`session.send`), approvals grab stdin in raw mode via the
|
|
21
|
+
* turn runs (`session.send`), approvals grab stdin in raw mode via the approval prompt
|
|
21
22
|
* — so no readline interface may be live at that moment. Creating a fresh
|
|
22
23
|
* interface per line, and closing it the instant we have input, guarantees the
|
|
23
24
|
* two never contend.
|
|
@@ -49,6 +50,18 @@ function readLine(io, prompt) {
|
|
|
49
50
|
});
|
|
50
51
|
});
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Render a non-fatal error inline (classified + formatted, the same 4-part
|
|
55
|
+
* shape as the fatal boundary) and return to the prompt — the REPL must survive
|
|
56
|
+
* a failed turn rather than exit.
|
|
57
|
+
*/
|
|
58
|
+
function printReplError(err) {
|
|
59
|
+
const cruxy = fromUnknown(err);
|
|
60
|
+
logger.print(formatError(cruxy, {
|
|
61
|
+
verbose: isVerbose(),
|
|
62
|
+
color: shouldUseColor(process.stdout),
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
52
65
|
/**
|
|
53
66
|
* Drive an interactive multi-turn session: prompt, read a line, dispatch slash
|
|
54
67
|
* commands or run a turn, repeat. Assistant text streams to stdout from within
|
|
@@ -83,7 +96,7 @@ export async function runInteractive(session, io = defaultIO()) {
|
|
|
83
96
|
logger.print(pc.dim(n ? `compacted ${n} older messages` : "nothing to compact yet"));
|
|
84
97
|
}
|
|
85
98
|
catch (err) {
|
|
86
|
-
|
|
99
|
+
printReplError(err);
|
|
87
100
|
}
|
|
88
101
|
continue;
|
|
89
102
|
}
|
package/dist/config/manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
+
import { configInvalid, configParse } from "../errors/index.js";
|
|
3
4
|
import { CruxyConfigSchema } from "./schema.js";
|
|
4
5
|
import { globalConfigPath, findProjectConfig } from "./paths.js";
|
|
5
6
|
function isPlainObject(v) {
|
|
@@ -23,13 +24,13 @@ function readJsonFile(path) {
|
|
|
23
24
|
try {
|
|
24
25
|
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
25
26
|
if (!isPlainObject(parsed)) {
|
|
26
|
-
throw new Error(
|
|
27
|
+
throw configParse(path, new Error("top-level value is not a JSON object"));
|
|
27
28
|
}
|
|
28
29
|
return parsed;
|
|
29
30
|
}
|
|
30
31
|
catch (err) {
|
|
31
32
|
if (err instanceof SyntaxError) {
|
|
32
|
-
throw
|
|
33
|
+
throw configParse(path, err);
|
|
33
34
|
}
|
|
34
35
|
throw err;
|
|
35
36
|
}
|
|
@@ -81,7 +82,7 @@ export function loadConfig(opts = {}) {
|
|
|
81
82
|
const issues = result.error.issues
|
|
82
83
|
.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`)
|
|
83
84
|
.join("\n");
|
|
84
|
-
throw
|
|
85
|
+
throw configInvalid(issues, sources.project ?? sources.global ?? undefined);
|
|
85
86
|
}
|
|
86
87
|
return { config: result.data, sources };
|
|
87
88
|
}
|
|
@@ -121,7 +122,7 @@ export function setValue(path, rawValue, file) {
|
|
|
121
122
|
const issues = check.error.issues
|
|
122
123
|
.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`)
|
|
123
124
|
.join("\n");
|
|
124
|
-
throw
|
|
125
|
+
throw configInvalid(issues, file);
|
|
125
126
|
}
|
|
126
127
|
mkdirSync(dirname(file), { recursive: true });
|
|
127
128
|
writeFileSync(file, JSON.stringify(current, null, 2) + "\n", "utf8");
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -52,10 +52,20 @@ export declare const ToolsConfigSchema: z.ZodObject<{
|
|
|
52
52
|
}>;
|
|
53
53
|
export declare const GitConfigSchema: z.ZodObject<{
|
|
54
54
|
autoCommit: z.ZodDefault<z.ZodBoolean>;
|
|
55
|
+
/** Branches cruxy never commits/pushes to directly (PR flow branches off
|
|
56
|
+
* first). `main` and `master` are always protected; these add to them. */
|
|
57
|
+
protectedBranches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
58
|
+
/** Default base branch for generated pull requests; falls back to the
|
|
59
|
+
* repo's default branch, then `main`, when unset. */
|
|
60
|
+
defaultBase: z.ZodOptional<z.ZodString>;
|
|
55
61
|
}, "strict", z.ZodTypeAny, {
|
|
56
62
|
autoCommit: boolean;
|
|
63
|
+
protectedBranches: string[];
|
|
64
|
+
defaultBase?: string | undefined;
|
|
57
65
|
}, {
|
|
58
66
|
autoCommit?: boolean | undefined;
|
|
67
|
+
protectedBranches?: string[] | undefined;
|
|
68
|
+
defaultBase?: string | undefined;
|
|
59
69
|
}>;
|
|
60
70
|
/** Execution bounds for the `run_command` shell tool (distinct from the
|
|
61
71
|
* `tools.shell` enable flag above). */
|
|
@@ -89,13 +99,18 @@ export declare const ContextConfigSchema: z.ZodObject<{
|
|
|
89
99
|
compactThreshold?: number | undefined;
|
|
90
100
|
keepRecentMessages?: number | undefined;
|
|
91
101
|
}>;
|
|
92
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* How tool-action approval is resolved. Only `prompt` exists: ask interactively
|
|
104
|
+
* and **deny by default** when non-interactive. There is deliberately no
|
|
105
|
+
* auto-approve / skip mode — the policy seam for a future CI mode lives in
|
|
106
|
+
* `src/approval` (see U.3), not behind a footgun flag.
|
|
107
|
+
*/
|
|
93
108
|
export declare const ApprovalConfigSchema: z.ZodObject<{
|
|
94
|
-
mode: z.ZodDefault<z.ZodEnum<["prompt"
|
|
109
|
+
mode: z.ZodDefault<z.ZodEnum<["prompt"]>>;
|
|
95
110
|
}, "strict", z.ZodTypeAny, {
|
|
96
|
-
mode: "
|
|
111
|
+
mode: "prompt";
|
|
97
112
|
}, {
|
|
98
|
-
mode?: "
|
|
113
|
+
mode?: "prompt" | undefined;
|
|
99
114
|
}>;
|
|
100
115
|
/**
|
|
101
116
|
* Codebase semantic index (C.17): the local embedding index that backs the
|
|
@@ -249,10 +264,20 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
249
264
|
}>>;
|
|
250
265
|
git: z.ZodDefault<z.ZodObject<{
|
|
251
266
|
autoCommit: z.ZodDefault<z.ZodBoolean>;
|
|
267
|
+
/** Branches cruxy never commits/pushes to directly (PR flow branches off
|
|
268
|
+
* first). `main` and `master` are always protected; these add to them. */
|
|
269
|
+
protectedBranches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
270
|
+
/** Default base branch for generated pull requests; falls back to the
|
|
271
|
+
* repo's default branch, then `main`, when unset. */
|
|
272
|
+
defaultBase: z.ZodOptional<z.ZodString>;
|
|
252
273
|
}, "strict", z.ZodTypeAny, {
|
|
253
274
|
autoCommit: boolean;
|
|
275
|
+
protectedBranches: string[];
|
|
276
|
+
defaultBase?: string | undefined;
|
|
254
277
|
}, {
|
|
255
278
|
autoCommit?: boolean | undefined;
|
|
279
|
+
protectedBranches?: string[] | undefined;
|
|
280
|
+
defaultBase?: string | undefined;
|
|
256
281
|
}>>;
|
|
257
282
|
shell: z.ZodDefault<z.ZodObject<{
|
|
258
283
|
/** Kill the command (and its process tree) after this many ms. */
|
|
@@ -284,11 +309,11 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
284
309
|
keepRecentMessages?: number | undefined;
|
|
285
310
|
}>>;
|
|
286
311
|
approval: z.ZodDefault<z.ZodObject<{
|
|
287
|
-
mode: z.ZodDefault<z.ZodEnum<["prompt"
|
|
312
|
+
mode: z.ZodDefault<z.ZodEnum<["prompt"]>>;
|
|
288
313
|
}, "strict", z.ZodTypeAny, {
|
|
289
|
-
mode: "
|
|
314
|
+
mode: "prompt";
|
|
290
315
|
}, {
|
|
291
|
-
mode?: "
|
|
316
|
+
mode?: "prompt" | undefined;
|
|
292
317
|
}>>;
|
|
293
318
|
index: z.ZodDefault<z.ZodObject<{
|
|
294
319
|
/** Master switch for `search_codebase` and `cruxy index`. */
|
|
@@ -411,6 +436,8 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
411
436
|
};
|
|
412
437
|
git: {
|
|
413
438
|
autoCommit: boolean;
|
|
439
|
+
protectedBranches: string[];
|
|
440
|
+
defaultBase?: string | undefined;
|
|
414
441
|
};
|
|
415
442
|
context: {
|
|
416
443
|
maxTokens: number;
|
|
@@ -418,7 +445,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
418
445
|
keepRecentMessages: number;
|
|
419
446
|
};
|
|
420
447
|
approval: {
|
|
421
|
-
mode: "
|
|
448
|
+
mode: "prompt";
|
|
422
449
|
};
|
|
423
450
|
index: {
|
|
424
451
|
search: {
|
|
@@ -466,6 +493,8 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
466
493
|
} | undefined;
|
|
467
494
|
git?: {
|
|
468
495
|
autoCommit?: boolean | undefined;
|
|
496
|
+
protectedBranches?: string[] | undefined;
|
|
497
|
+
defaultBase?: string | undefined;
|
|
469
498
|
} | undefined;
|
|
470
499
|
context?: {
|
|
471
500
|
maxTokens?: number | undefined;
|
|
@@ -473,7 +502,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
473
502
|
keepRecentMessages?: number | undefined;
|
|
474
503
|
} | undefined;
|
|
475
504
|
approval?: {
|
|
476
|
-
mode?: "
|
|
505
|
+
mode?: "prompt" | undefined;
|
|
477
506
|
} | undefined;
|
|
478
507
|
index?: {
|
|
479
508
|
search?: {
|
package/dist/config/schema.js
CHANGED
|
@@ -40,6 +40,12 @@ export const ToolsConfigSchema = z
|
|
|
40
40
|
export const GitConfigSchema = z
|
|
41
41
|
.object({
|
|
42
42
|
autoCommit: z.boolean().default(false),
|
|
43
|
+
/** Branches cruxy never commits/pushes to directly (PR flow branches off
|
|
44
|
+
* first). `main` and `master` are always protected; these add to them. */
|
|
45
|
+
protectedBranches: z.array(z.string()).default([]),
|
|
46
|
+
/** Default base branch for generated pull requests; falls back to the
|
|
47
|
+
* repo's default branch, then `main`, when unset. */
|
|
48
|
+
defaultBase: z.string().optional(),
|
|
43
49
|
})
|
|
44
50
|
.strict();
|
|
45
51
|
/** Execution bounds for the `run_command` shell tool (distinct from the
|
|
@@ -64,12 +70,15 @@ export const ContextConfigSchema = z
|
|
|
64
70
|
keepRecentMessages: z.number().int().positive().default(6),
|
|
65
71
|
})
|
|
66
72
|
.strict();
|
|
67
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* How tool-action approval is resolved. Only `prompt` exists: ask interactively
|
|
75
|
+
* and **deny by default** when non-interactive. There is deliberately no
|
|
76
|
+
* auto-approve / skip mode — the policy seam for a future CI mode lives in
|
|
77
|
+
* `src/approval` (see U.3), not behind a footgun flag.
|
|
78
|
+
*/
|
|
68
79
|
export const ApprovalConfigSchema = z
|
|
69
80
|
.object({
|
|
70
|
-
|
|
71
|
-
// every action unattended (CI / explicit opt-in).
|
|
72
|
-
mode: z.enum(["prompt", "auto"]).default("prompt"),
|
|
81
|
+
mode: z.enum(["prompt"]).default("prompt"),
|
|
73
82
|
})
|
|
74
83
|
.strict();
|
|
75
84
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Formatter } from "./format.js";
|
|
2
|
+
import { CruxyError } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The single top-level error boundary. {@link handleFatal} classifies any thrown
|
|
5
|
+
* value into a {@link CruxyError}, formats it, writes it to stderr, and exits
|
|
6
|
+
* with the error's stable exit code. Nothing escapes to a raw Node stack trace:
|
|
7
|
+
* an unknown error becomes {@link internal} (CRUXY_E_INTERNAL) with a
|
|
8
|
+
* bug-report hint, and the underlying stack is shown only under `--verbose`.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Normalize any thrown value into a {@link CruxyError}:
|
|
12
|
+
* - already a CruxyError → returned as-is;
|
|
13
|
+
* - a Commander parse error → a usage error (CRUXY_E_USAGE);
|
|
14
|
+
* - a known provider/transport error → its mapped code;
|
|
15
|
+
* - anything else → CRUXY_E_INTERNAL, preserving the original as `underlying`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fromUnknown(err: unknown): CruxyError;
|
|
18
|
+
/** A Commander "error" that's actually success (`--help`, `--version`). */
|
|
19
|
+
export declare function isCommanderSuccess(err: unknown): boolean;
|
|
20
|
+
export interface HandleFatalOptions {
|
|
21
|
+
/** Show the underlying error/stack. Defaults to {@link isVerbose}(). */
|
|
22
|
+
verbose?: boolean;
|
|
23
|
+
/** Override the formatter (e.g. a future JSON formatter). */
|
|
24
|
+
formatter?: Formatter;
|
|
25
|
+
/** Force color on/off. Defaults to {@link shouldUseColor}(). */
|
|
26
|
+
color?: boolean;
|
|
27
|
+
/** Sink for the formatted output (default: stderr). For tests. */
|
|
28
|
+
write?: (text: string) => void;
|
|
29
|
+
/** Process exit (default: process.exit). For tests. */
|
|
30
|
+
exit?: (code: number) => never;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Classify → format → print → exit. Install this as the *only* catch at the CLI
|
|
34
|
+
* entry. `write`/`exit` are injectable so the boundary is testable without
|
|
35
|
+
* touching the real process.
|
|
36
|
+
*/
|
|
37
|
+
export declare function handleFatal(err: unknown, opts?: HandleFatalOptions): never;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the invocation requested verbose output: `--verbose`,
|
|
40
|
+
* `--log-level debug`, `DEBUG`, or `CRUXY_LOG_LEVEL=debug`. Read from argv/env so
|
|
41
|
+
* it works even when a failure happens before the CLI finishes parsing flags.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isVerbose(argv?: string[], env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { CommanderError } from "commander";
|
|
2
|
+
import { classifyProviderError, internal, usageError } from "./constructors.js";
|
|
3
|
+
import { shouldUseColor, terminalFormatter } from "./format.js";
|
|
4
|
+
import { CruxyError } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* The single top-level error boundary. {@link handleFatal} classifies any thrown
|
|
7
|
+
* value into a {@link CruxyError}, formats it, writes it to stderr, and exits
|
|
8
|
+
* with the error's stable exit code. Nothing escapes to a raw Node stack trace:
|
|
9
|
+
* an unknown error becomes {@link internal} (CRUXY_E_INTERNAL) with a
|
|
10
|
+
* bug-report hint, and the underlying stack is shown only under `--verbose`.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Normalize any thrown value into a {@link CruxyError}:
|
|
14
|
+
* - already a CruxyError → returned as-is;
|
|
15
|
+
* - a Commander parse error → a usage error (CRUXY_E_USAGE);
|
|
16
|
+
* - a known provider/transport error → its mapped code;
|
|
17
|
+
* - anything else → CRUXY_E_INTERNAL, preserving the original as `underlying`.
|
|
18
|
+
*/
|
|
19
|
+
export function fromUnknown(err) {
|
|
20
|
+
if (err instanceof CruxyError)
|
|
21
|
+
return err;
|
|
22
|
+
if (err instanceof CommanderError) {
|
|
23
|
+
return usageError(stripErrorPrefix(err.message));
|
|
24
|
+
}
|
|
25
|
+
return classifyProviderError(err) ?? internal(err);
|
|
26
|
+
}
|
|
27
|
+
/** A Commander "error" that's actually success (`--help`, `--version`). */
|
|
28
|
+
export function isCommanderSuccess(err) {
|
|
29
|
+
return err instanceof CommanderError && err.exitCode === 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Classify → format → print → exit. Install this as the *only* catch at the CLI
|
|
33
|
+
* entry. `write`/`exit` are injectable so the boundary is testable without
|
|
34
|
+
* touching the real process.
|
|
35
|
+
*/
|
|
36
|
+
export function handleFatal(err, opts = {}) {
|
|
37
|
+
const cruxy = fromUnknown(err);
|
|
38
|
+
const verbose = opts.verbose ?? isVerbose();
|
|
39
|
+
const color = opts.color ?? shouldUseColor();
|
|
40
|
+
const formatter = opts.formatter ?? terminalFormatter;
|
|
41
|
+
const write = opts.write ?? ((text) => void process.stderr.write(text));
|
|
42
|
+
const exit = opts.exit ?? ((code) => process.exit(code));
|
|
43
|
+
write(`${formatter.format(cruxy, { verbose, color })}\n`);
|
|
44
|
+
return exit(cruxy.exitCode);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether the invocation requested verbose output: `--verbose`,
|
|
48
|
+
* `--log-level debug`, `DEBUG`, or `CRUXY_LOG_LEVEL=debug`. Read from argv/env so
|
|
49
|
+
* it works even when a failure happens before the CLI finishes parsing flags.
|
|
50
|
+
*/
|
|
51
|
+
export function isVerbose(argv = process.argv, env = process.env) {
|
|
52
|
+
if (argv.includes("--verbose"))
|
|
53
|
+
return true;
|
|
54
|
+
if (argv.includes("--log-level=debug"))
|
|
55
|
+
return true;
|
|
56
|
+
const idx = argv.indexOf("--log-level");
|
|
57
|
+
if (idx !== -1 && argv[idx + 1] === "debug")
|
|
58
|
+
return true;
|
|
59
|
+
if (env.CRUXY_LOG_LEVEL === "debug")
|
|
60
|
+
return true;
|
|
61
|
+
const debug = env.DEBUG;
|
|
62
|
+
if (debug !== undefined &&
|
|
63
|
+
debug !== "" &&
|
|
64
|
+
debug !== "0" &&
|
|
65
|
+
debug !== "false") {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/** Strip Commander's leading "error: " so the title reads cleanly. */
|
|
71
|
+
function stripErrorPrefix(message) {
|
|
72
|
+
return message.replace(/^error:\s*/i, "");
|
|
73
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CruxyError } from "./types.js";
|
|
2
|
+
/** Best-effort human message for an arbitrary thrown value. */
|
|
3
|
+
export declare function messageOf(underlying: unknown): string | undefined;
|
|
4
|
+
export declare function usageError(title: string, nextSteps?: string[]): CruxyError;
|
|
5
|
+
export declare function configKeyUnknown(key: string): CruxyError;
|
|
6
|
+
export declare function providerUnsupported(provider: string): CruxyError;
|
|
7
|
+
export declare function configParse(path: string, underlying?: unknown): CruxyError;
|
|
8
|
+
export declare function configInvalid(issues: string, path?: string): CruxyError;
|
|
9
|
+
export declare function authMissingKey(provider: string, envVar: string): CruxyError;
|
|
10
|
+
export declare function authInvalid(underlying?: unknown): CruxyError;
|
|
11
|
+
export declare function gatewayUnreachable(underlying?: unknown): CruxyError;
|
|
12
|
+
export declare function apiError(underlying?: unknown): CruxyError;
|
|
13
|
+
export declare function apiRateLimit(underlying?: unknown): CruxyError;
|
|
14
|
+
export declare function apiOverloaded(underlying?: unknown): CruxyError;
|
|
15
|
+
export declare function budgetExhausted(underlying?: unknown): CruxyError;
|
|
16
|
+
export declare function fileNotFound(path: string, underlying?: unknown): CruxyError;
|
|
17
|
+
export declare function permissionDenied(path: string, underlying?: unknown): CruxyError;
|
|
18
|
+
export declare function indexEmbedderUnavailable(underlying?: unknown): CruxyError;
|
|
19
|
+
export declare function indexStoreUnavailable(underlying?: unknown): CruxyError;
|
|
20
|
+
export declare function indexFailed(underlying?: unknown): CruxyError;
|
|
21
|
+
/**
|
|
22
|
+
* A side-effecting action needs approval but cruxy can't ask (non-interactive,
|
|
23
|
+
* no policy). Default-deny — never auto-approve. A distinct exit code (10) so CI
|
|
24
|
+
* can tell "needed approval" apart from a usage error.
|
|
25
|
+
*/
|
|
26
|
+
export declare function approvalRequired(summary: string): CruxyError;
|
|
27
|
+
/**
|
|
28
|
+
* No forge token could be resolved (PR generation, C.15). The chain is env →
|
|
29
|
+
* `gh auth token` → fail. We never prompt for, store, or persist a token, so the
|
|
30
|
+
* fix is always to provide one in the environment.
|
|
31
|
+
*/
|
|
32
|
+
export declare function forgeAuth(host?: string): CruxyError;
|
|
33
|
+
/**
|
|
34
|
+
* A commit/push was attempted on a protected branch (`main`/`master`/configured).
|
|
35
|
+
* The PR flow must branch off first; this is the last-line guard.
|
|
36
|
+
*/
|
|
37
|
+
export declare function gitProtectedBranch(branch: string): CruxyError;
|
|
38
|
+
/** The forge REST API returned an error (non-auth) while opening a PR. */
|
|
39
|
+
export declare function forgeApi(title: string, underlying?: unknown, meta?: Record<string, unknown>): CruxyError;
|
|
40
|
+
/**
|
|
41
|
+
* `git push` failed — most often the husky `pre-push` verify hook (build ·
|
|
42
|
+
* typecheck · lint · test) or a rejected non-fast-forward. We never `--force` or
|
|
43
|
+
* `--no-verify`, so the underlying reason is surfaced verbatim.
|
|
44
|
+
*/
|
|
45
|
+
export declare function gitPushFailed(branch: string, stderr?: string): CruxyError;
|
|
46
|
+
export declare function internal(underlying?: unknown): CruxyError;
|
|
47
|
+
/**
|
|
48
|
+
* Map a known provider/transport error (from `@cruxy/sdk`) to a typed
|
|
49
|
+
* {@link CruxyError}, or `null` if it isn't one. Order matters: specific
|
|
50
|
+
* subclasses before the `ApiError` base.
|
|
51
|
+
*/
|
|
52
|
+
export declare function classifyProviderError(underlying: unknown): CruxyError | null;
|