@robin7331/papyrus-cli 0.1.6 → 0.1.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/README.md +13 -25
- package/dist/cli.js +14 -14
- package/dist/cliHelpers.d.ts +1 -3
- package/dist/cliHelpers.js +5 -17
- package/package.json +1 -1
- package/src/cli.ts +15 -13
- package/src/cliHelpers.ts +6 -23
- package/test/cliHelpers.test.ts +19 -31
package/README.md
CHANGED
|
@@ -27,20 +27,20 @@ papyrus --help
|
|
|
27
27
|
# Show installed CLI version
|
|
28
28
|
papyrus --version
|
|
29
29
|
|
|
30
|
-
# Single file (
|
|
30
|
+
# Single file (default behavior; if no API key is found, Papyrus prompts you to paste one)
|
|
31
31
|
papyrus ./path/to/input.pdf
|
|
32
32
|
|
|
33
33
|
# Single file with explicit format/output/model
|
|
34
34
|
papyrus ./path/to/input.pdf --format md --output ./out/result.md --model gpt-4o-mini
|
|
35
35
|
|
|
36
|
-
#
|
|
36
|
+
# Default conversion with extra instructions
|
|
37
37
|
papyrus ./path/to/input.pdf --instructions "Prioritize table accuracy." --format txt
|
|
38
38
|
|
|
39
|
-
# Prompt
|
|
40
|
-
papyrus ./path/to/input.pdf --
|
|
39
|
+
# Prompt conversion (inline prompt)
|
|
40
|
+
papyrus ./path/to/input.pdf --prompt "Extract all invoice line items as bullet points." --format md
|
|
41
41
|
|
|
42
|
-
# Prompt
|
|
43
|
-
papyrus ./path/to/input.pdf --
|
|
42
|
+
# Prompt conversion (prompt file)
|
|
43
|
+
papyrus ./path/to/input.pdf --prompt-file ./my-prompt.txt --format txt
|
|
44
44
|
|
|
45
45
|
# Folder mode (recursive scan, asks for confirmation)
|
|
46
46
|
papyrus ./path/to/folder
|
|
@@ -132,46 +132,34 @@ Example:
|
|
|
132
132
|
papyrus ./docs --output ./converted
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
### `--mode <mode>`
|
|
136
|
-
|
|
137
|
-
Conversion mode:
|
|
138
|
-
- `auto` (default): built-in conversion behavior.
|
|
139
|
-
- `prompt`: use your own prompt via `--prompt` or `--prompt-file`.
|
|
140
|
-
|
|
141
|
-
Example:
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
papyrus ./docs/invoice.pdf --mode prompt --prompt "Extract all line items."
|
|
145
|
-
```
|
|
146
|
-
|
|
147
135
|
### `--instructions <text>`
|
|
148
136
|
|
|
149
|
-
Additional conversion instructions
|
|
137
|
+
Additional conversion instructions for default conversion behavior. Cannot be combined with `--prompt` or `--prompt-file`.
|
|
150
138
|
|
|
151
139
|
Example:
|
|
152
140
|
|
|
153
141
|
```bash
|
|
154
|
-
papyrus ./docs/invoice.pdf --
|
|
142
|
+
papyrus ./docs/invoice.pdf --instructions "Keep table columns aligned."
|
|
155
143
|
```
|
|
156
144
|
|
|
157
145
|
### `--prompt <text>`
|
|
158
146
|
|
|
159
|
-
Inline prompt text for
|
|
147
|
+
Inline prompt text for prompt-based conversion. Must be non-empty. Use exactly one of `--prompt` or `--prompt-file`.
|
|
160
148
|
|
|
161
149
|
Example:
|
|
162
150
|
|
|
163
151
|
```bash
|
|
164
|
-
papyrus ./docs/invoice.pdf --
|
|
152
|
+
papyrus ./docs/invoice.pdf --prompt "Summarize payment terms."
|
|
165
153
|
```
|
|
166
154
|
|
|
167
155
|
### `--prompt-file <path>`
|
|
168
156
|
|
|
169
|
-
Path to a text file containing the prompt for
|
|
157
|
+
Path to a text file containing the prompt for prompt-based conversion. File must contain non-empty text. Use exactly one of `--prompt` or `--prompt-file`.
|
|
170
158
|
|
|
171
159
|
Example:
|
|
172
160
|
|
|
173
161
|
```bash
|
|
174
|
-
papyrus ./docs/invoice.pdf --
|
|
162
|
+
papyrus ./docs/invoice.pdf --prompt-file ./my-prompt.txt
|
|
175
163
|
```
|
|
176
164
|
|
|
177
165
|
### `-m, --model <model>`
|
|
@@ -206,7 +194,7 @@ papyrus ./docs --yes
|
|
|
206
194
|
|
|
207
195
|
## Notes
|
|
208
196
|
|
|
209
|
-
- In `
|
|
197
|
+
- In default conversion (without `--prompt`/`--prompt-file`) and without `--format`, the model returns structured JSON with `format` + `content`.
|
|
210
198
|
- Single-file input now also shows a live worker lane (spinner in TTY) while conversion is running.
|
|
211
199
|
- Folder input is scanned recursively for `.pdf` files and processed in parallel.
|
|
212
200
|
- In folder mode, `--output` must be a directory path and mirrored subfolders are preserved.
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { clearStoredApiKey, getConfigFilePath, getStoredApiKey, maskApiKey, setStoredApiKey } from "./config.js";
|
|
8
8
|
import { convertPdf } from "./openaiPdfToMarkdown.js";
|
|
9
|
-
import { defaultOutputPath, formatDurationMs, isPdfPath, looksLikeFileOutput, parseConcurrency, parseFormat,
|
|
9
|
+
import { defaultOutputPath, formatDurationMs, isPdfPath, looksLikeFileOutput, parseConcurrency, parseFormat, resolveFolderOutputPath, truncate, validateOptionCombination } from "./cliHelpers.js";
|
|
10
10
|
const program = new Command();
|
|
11
11
|
const configFilePath = getConfigFilePath();
|
|
12
12
|
const OPENAI_API_KEYS_URL = "https://platform.openai.com/settings/organization/api-keys";
|
|
@@ -20,24 +20,24 @@ program
|
|
|
20
20
|
.option("-m, --model <model>", "OpenAI model to use", "gpt-4o-mini")
|
|
21
21
|
.option("--concurrency <n>", "Max parallel workers for folder input (default: 10)", parseConcurrency)
|
|
22
22
|
.option("-y, --yes", "Skip confirmation prompt in folder mode")
|
|
23
|
-
.option("--mode <mode>", "Conversion mode: auto or prompt", parseMode, "auto")
|
|
24
23
|
.option("--format <format>", "Output format override: md or txt", parseFormat)
|
|
25
|
-
.option("--instructions <text>", "Additional conversion instructions
|
|
26
|
-
.option("--prompt <text>", "Custom prompt text
|
|
27
|
-
.option("--prompt-file <path>", "Path to file containing prompt text
|
|
24
|
+
.option("--instructions <text>", "Additional conversion instructions (only when not using --prompt/--prompt-file)")
|
|
25
|
+
.option("--prompt <text>", "Custom prompt text (enables prompt mode)")
|
|
26
|
+
.option("--prompt-file <path>", "Path to file containing prompt text (enables prompt mode)")
|
|
28
27
|
.action(async (input, options) => {
|
|
29
28
|
const inputPath = resolve(input);
|
|
30
29
|
const startedAt = Date.now();
|
|
31
30
|
try {
|
|
32
31
|
validateOptionCombination(options);
|
|
33
32
|
const promptText = await resolvePromptText(options);
|
|
33
|
+
const conversionMode = resolveConversionMode(promptText);
|
|
34
34
|
const inputKind = await detectInputKind(inputPath);
|
|
35
35
|
let usageTotals = emptyUsage();
|
|
36
36
|
if (inputKind === "file") {
|
|
37
|
-
usageTotals = await processSingleFile(inputPath, options, promptText);
|
|
37
|
+
usageTotals = await processSingleFile(inputPath, options, conversionMode, promptText);
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
|
-
const summary = await processFolder(inputPath, options, promptText);
|
|
40
|
+
const summary = await processFolder(inputPath, options, conversionMode, promptText);
|
|
41
41
|
usageTotals = summary.usage;
|
|
42
42
|
if (!summary.cancelled && summary.failed > 0) {
|
|
43
43
|
process.exitCode = 1;
|
|
@@ -112,7 +112,7 @@ program.parseAsync(process.argv).catch((error) => {
|
|
|
112
112
|
console.error(`Command failed: ${message}`);
|
|
113
113
|
process.exitCode = 1;
|
|
114
114
|
});
|
|
115
|
-
async function processSingleFile(inputPath, options, promptText) {
|
|
115
|
+
async function processSingleFile(inputPath, options, mode, promptText) {
|
|
116
116
|
if (!isPdfPath(inputPath)) {
|
|
117
117
|
throw new Error("Input file must have a .pdf extension.");
|
|
118
118
|
}
|
|
@@ -131,7 +131,7 @@ async function processSingleFile(inputPath, options, promptText) {
|
|
|
131
131
|
const result = await convertPdf({
|
|
132
132
|
inputPath,
|
|
133
133
|
model: options.model,
|
|
134
|
-
mode
|
|
134
|
+
mode,
|
|
135
135
|
format: options.format,
|
|
136
136
|
instructions: options.instructions,
|
|
137
137
|
promptText
|
|
@@ -164,7 +164,7 @@ async function processSingleFile(inputPath, options, promptText) {
|
|
|
164
164
|
workerDashboard?.stop();
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
async function processFolder(inputDir, options, promptText) {
|
|
167
|
+
async function processFolder(inputDir, options, mode, promptText) {
|
|
168
168
|
if (options.output && looksLikeFileOutput(options.output)) {
|
|
169
169
|
throw new Error("In folder mode, --output must be a directory path (not a .md/.txt file path).");
|
|
170
170
|
}
|
|
@@ -200,7 +200,7 @@ async function processFolder(inputDir, options, promptText) {
|
|
|
200
200
|
const result = await convertPdf({
|
|
201
201
|
inputPath: filePath,
|
|
202
202
|
model: options.model,
|
|
203
|
-
mode
|
|
203
|
+
mode,
|
|
204
204
|
format: options.format,
|
|
205
205
|
instructions: options.instructions,
|
|
206
206
|
promptText
|
|
@@ -250,9 +250,6 @@ async function processFolder(inputDir, options, promptText) {
|
|
|
250
250
|
return { total: files.length, succeeded, failed, cancelled: false, usage };
|
|
251
251
|
}
|
|
252
252
|
async function resolvePromptText(options) {
|
|
253
|
-
if (options.mode !== "prompt") {
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
253
|
if (options.prompt) {
|
|
257
254
|
const prompt = options.prompt.trim();
|
|
258
255
|
if (!prompt) {
|
|
@@ -270,6 +267,9 @@ async function resolvePromptText(options) {
|
|
|
270
267
|
}
|
|
271
268
|
return promptFromFile;
|
|
272
269
|
}
|
|
270
|
+
function resolveConversionMode(promptText) {
|
|
271
|
+
return promptText ? "prompt" : "auto";
|
|
272
|
+
}
|
|
273
273
|
async function handleConfigInit(options) {
|
|
274
274
|
const existingKey = await getStoredApiKey();
|
|
275
275
|
if (existingKey && !options.force) {
|
package/dist/cliHelpers.d.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type OutputFormat } from "./openaiPdfToMarkdown.js";
|
|
2
2
|
export type CliOptions = {
|
|
3
3
|
output?: string;
|
|
4
4
|
model: string;
|
|
5
5
|
concurrency?: number;
|
|
6
6
|
yes?: boolean;
|
|
7
|
-
mode: ConversionMode;
|
|
8
7
|
format?: OutputFormat;
|
|
9
8
|
instructions?: string;
|
|
10
9
|
prompt?: string;
|
|
11
10
|
promptFile?: string;
|
|
12
11
|
};
|
|
13
|
-
export declare function parseMode(value: string): ConversionMode;
|
|
14
12
|
export declare function parseFormat(value: string): OutputFormat;
|
|
15
13
|
export declare function parseConcurrency(value: string): number;
|
|
16
14
|
export declare function validateOptionCombination(options: CliOptions): void;
|
package/dist/cliHelpers.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { InvalidArgumentError } from "commander";
|
|
2
2
|
import { basename, dirname, extname, join, relative } from "node:path";
|
|
3
|
-
export function parseMode(value) {
|
|
4
|
-
if (value === "auto" || value === "prompt") {
|
|
5
|
-
return value;
|
|
6
|
-
}
|
|
7
|
-
throw new InvalidArgumentError("Mode must be either 'auto' or 'prompt'.");
|
|
8
|
-
}
|
|
9
3
|
export function parseFormat(value) {
|
|
10
4
|
if (value === "md" || value === "txt") {
|
|
11
5
|
return value;
|
|
@@ -20,18 +14,12 @@ export function parseConcurrency(value) {
|
|
|
20
14
|
return parsed;
|
|
21
15
|
}
|
|
22
16
|
export function validateOptionCombination(options) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
throw new Error("Prompt mode requires exactly one of --prompt or --prompt-file.");
|
|
27
|
-
}
|
|
28
|
-
if (options.instructions) {
|
|
29
|
-
throw new Error("--instructions is only supported in auto mode.");
|
|
30
|
-
}
|
|
31
|
-
return;
|
|
17
|
+
const promptSourceCount = Number(Boolean(options.prompt)) + Number(Boolean(options.promptFile));
|
|
18
|
+
if (promptSourceCount > 1) {
|
|
19
|
+
throw new Error("Use exactly one of --prompt or --prompt-file.");
|
|
32
20
|
}
|
|
33
|
-
if (
|
|
34
|
-
throw new Error("--
|
|
21
|
+
if (promptSourceCount === 1 && options.instructions) {
|
|
22
|
+
throw new Error("--instructions cannot be combined with --prompt or --prompt-file.");
|
|
35
23
|
}
|
|
36
24
|
}
|
|
37
25
|
export function defaultOutputPath(inputPath, format) {
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "./config.js";
|
|
15
15
|
import {
|
|
16
16
|
convertPdf,
|
|
17
|
+
type ConversionMode,
|
|
17
18
|
type ConvertUsage
|
|
18
19
|
} from "./openaiPdfToMarkdown.js";
|
|
19
20
|
import {
|
|
@@ -23,7 +24,6 @@ import {
|
|
|
23
24
|
looksLikeFileOutput,
|
|
24
25
|
parseConcurrency,
|
|
25
26
|
parseFormat,
|
|
26
|
-
parseMode,
|
|
27
27
|
resolveFolderOutputPath,
|
|
28
28
|
truncate,
|
|
29
29
|
type CliOptions,
|
|
@@ -52,14 +52,13 @@ program
|
|
|
52
52
|
parseConcurrency
|
|
53
53
|
)
|
|
54
54
|
.option("-y, --yes", "Skip confirmation prompt in folder mode")
|
|
55
|
-
.option("--mode <mode>", "Conversion mode: auto or prompt", parseMode, "auto")
|
|
56
55
|
.option("--format <format>", "Output format override: md or txt", parseFormat)
|
|
57
56
|
.option(
|
|
58
57
|
"--instructions <text>",
|
|
59
|
-
"Additional conversion instructions
|
|
58
|
+
"Additional conversion instructions (only when not using --prompt/--prompt-file)"
|
|
60
59
|
)
|
|
61
|
-
.option("--prompt <text>", "Custom prompt text
|
|
62
|
-
.option("--prompt-file <path>", "Path to file containing prompt text
|
|
60
|
+
.option("--prompt <text>", "Custom prompt text (enables prompt mode)")
|
|
61
|
+
.option("--prompt-file <path>", "Path to file containing prompt text (enables prompt mode)")
|
|
63
62
|
.action(async (input: string, options: CliOptions) => {
|
|
64
63
|
const inputPath = resolve(input);
|
|
65
64
|
const startedAt = Date.now();
|
|
@@ -68,13 +67,14 @@ program
|
|
|
68
67
|
validateOptionCombination(options);
|
|
69
68
|
|
|
70
69
|
const promptText = await resolvePromptText(options);
|
|
70
|
+
const conversionMode = resolveConversionMode(promptText);
|
|
71
71
|
const inputKind = await detectInputKind(inputPath);
|
|
72
72
|
let usageTotals: ConvertUsage = emptyUsage();
|
|
73
73
|
|
|
74
74
|
if (inputKind === "file") {
|
|
75
|
-
usageTotals = await processSingleFile(inputPath, options, promptText);
|
|
75
|
+
usageTotals = await processSingleFile(inputPath, options, conversionMode, promptText);
|
|
76
76
|
} else {
|
|
77
|
-
const summary = await processFolder(inputPath, options, promptText);
|
|
77
|
+
const summary = await processFolder(inputPath, options, conversionMode, promptText);
|
|
78
78
|
usageTotals = summary.usage;
|
|
79
79
|
if (!summary.cancelled && summary.failed > 0) {
|
|
80
80
|
process.exitCode = 1;
|
|
@@ -157,6 +157,7 @@ program.parseAsync(process.argv).catch((error: unknown) => {
|
|
|
157
157
|
async function processSingleFile(
|
|
158
158
|
inputPath: string,
|
|
159
159
|
options: CliOptions,
|
|
160
|
+
mode: ConversionMode,
|
|
160
161
|
promptText?: string
|
|
161
162
|
): Promise<ConvertUsage> {
|
|
162
163
|
if (!isPdfPath(inputPath)) {
|
|
@@ -180,7 +181,7 @@ async function processSingleFile(
|
|
|
180
181
|
const result = await convertPdf({
|
|
181
182
|
inputPath,
|
|
182
183
|
model: options.model,
|
|
183
|
-
mode
|
|
184
|
+
mode,
|
|
184
185
|
format: options.format,
|
|
185
186
|
instructions: options.instructions,
|
|
186
187
|
promptText
|
|
@@ -237,6 +238,7 @@ type FolderSummary = {
|
|
|
237
238
|
async function processFolder(
|
|
238
239
|
inputDir: string,
|
|
239
240
|
options: CliOptions,
|
|
241
|
+
mode: ConversionMode,
|
|
240
242
|
promptText?: string
|
|
241
243
|
): Promise<FolderSummary> {
|
|
242
244
|
if (options.output && looksLikeFileOutput(options.output)) {
|
|
@@ -282,7 +284,7 @@ async function processFolder(
|
|
|
282
284
|
const result = await convertPdf({
|
|
283
285
|
inputPath: filePath,
|
|
284
286
|
model: options.model,
|
|
285
|
-
mode
|
|
287
|
+
mode,
|
|
286
288
|
format: options.format,
|
|
287
289
|
instructions: options.instructions,
|
|
288
290
|
promptText
|
|
@@ -347,10 +349,6 @@ async function processFolder(
|
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
async function resolvePromptText(options: CliOptions): Promise<string | undefined> {
|
|
350
|
-
if (options.mode !== "prompt") {
|
|
351
|
-
return undefined;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
352
|
if (options.prompt) {
|
|
355
353
|
const prompt = options.prompt.trim();
|
|
356
354
|
if (!prompt) {
|
|
@@ -373,6 +371,10 @@ async function resolvePromptText(options: CliOptions): Promise<string | undefine
|
|
|
373
371
|
return promptFromFile;
|
|
374
372
|
}
|
|
375
373
|
|
|
374
|
+
function resolveConversionMode(promptText: string | undefined): ConversionMode {
|
|
375
|
+
return promptText ? "prompt" : "auto";
|
|
376
|
+
}
|
|
377
|
+
|
|
376
378
|
async function handleConfigInit(options: ConfigInitOptions): Promise<void> {
|
|
377
379
|
const existingKey = await getStoredApiKey();
|
|
378
380
|
if (existingKey && !options.force) {
|
package/src/cliHelpers.ts
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
import { InvalidArgumentError } from "commander";
|
|
2
2
|
import { basename, dirname, extname, join, relative } from "node:path";
|
|
3
|
-
import { type
|
|
3
|
+
import { type OutputFormat } from "./openaiPdfToMarkdown.js";
|
|
4
4
|
|
|
5
5
|
export type CliOptions = {
|
|
6
6
|
output?: string;
|
|
7
7
|
model: string;
|
|
8
8
|
concurrency?: number;
|
|
9
9
|
yes?: boolean;
|
|
10
|
-
mode: ConversionMode;
|
|
11
10
|
format?: OutputFormat;
|
|
12
11
|
instructions?: string;
|
|
13
12
|
prompt?: string;
|
|
14
13
|
promptFile?: string;
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
export function parseMode(value: string): ConversionMode {
|
|
18
|
-
if (value === "auto" || value === "prompt") {
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
throw new InvalidArgumentError("Mode must be either 'auto' or 'prompt'.");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
export function parseFormat(value: string): OutputFormat {
|
|
26
17
|
if (value === "md" || value === "txt") {
|
|
27
18
|
return value;
|
|
@@ -40,21 +31,13 @@ export function parseConcurrency(value: string): number {
|
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
export function validateOptionCombination(options: CliOptions): void {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
throw new Error("Prompt mode requires exactly one of --prompt or --prompt-file.");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (options.instructions) {
|
|
50
|
-
throw new Error("--instructions is only supported in auto mode.");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return;
|
|
34
|
+
const promptSourceCount = Number(Boolean(options.prompt)) + Number(Boolean(options.promptFile));
|
|
35
|
+
if (promptSourceCount > 1) {
|
|
36
|
+
throw new Error("Use exactly one of --prompt or --prompt-file.");
|
|
54
37
|
}
|
|
55
38
|
|
|
56
|
-
if (
|
|
57
|
-
throw new Error("--
|
|
39
|
+
if (promptSourceCount === 1 && options.instructions) {
|
|
40
|
+
throw new Error("--instructions cannot be combined with --prompt or --prompt-file.");
|
|
58
41
|
}
|
|
59
42
|
}
|
|
60
43
|
|
package/test/cliHelpers.test.ts
CHANGED
|
@@ -8,22 +8,12 @@ import {
|
|
|
8
8
|
looksLikeFileOutput,
|
|
9
9
|
parseConcurrency,
|
|
10
10
|
parseFormat,
|
|
11
|
-
parseMode,
|
|
12
11
|
resolveFolderOutputPath,
|
|
13
12
|
truncate,
|
|
14
13
|
validateOptionCombination,
|
|
15
14
|
type CliOptions
|
|
16
15
|
} from "../src/cliHelpers.js";
|
|
17
16
|
|
|
18
|
-
test("parseMode accepts valid values", () => {
|
|
19
|
-
assert.equal(parseMode("auto"), "auto");
|
|
20
|
-
assert.equal(parseMode("prompt"), "prompt");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("parseMode rejects invalid values", () => {
|
|
24
|
-
assert.throws(() => parseMode("invalid"), InvalidArgumentError);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
17
|
test("parseFormat accepts valid values", () => {
|
|
28
18
|
assert.equal(parseFormat("md"), "md");
|
|
29
19
|
assert.equal(parseFormat("txt"), "txt");
|
|
@@ -45,42 +35,40 @@ test("parseConcurrency rejects invalid values", () => {
|
|
|
45
35
|
assert.throws(() => parseConcurrency("abc"), InvalidArgumentError);
|
|
46
36
|
});
|
|
47
37
|
|
|
48
|
-
test("validateOptionCombination
|
|
38
|
+
test("validateOptionCombination allows default auto behavior without prompt flags", () => {
|
|
49
39
|
const base: CliOptions = {
|
|
50
|
-
model: "gpt-4o-mini"
|
|
51
|
-
mode: "prompt"
|
|
40
|
+
model: "gpt-4o-mini"
|
|
52
41
|
};
|
|
53
42
|
|
|
54
|
-
assert.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
assert.doesNotThrow(() => validateOptionCombination(base));
|
|
44
|
+
assert.doesNotThrow(() => validateOptionCombination({ ...base, instructions: "Extra formatting rules" }));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("validateOptionCombination treats --prompt and --prompt-file as mutually exclusive", () => {
|
|
48
|
+
const base: CliOptions = {
|
|
49
|
+
model: "gpt-4o-mini"
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
assert.doesNotThrow(() => validateOptionCombination({ ...base, prompt: "Convert" }));
|
|
59
53
|
assert.doesNotThrow(() => validateOptionCombination({ ...base, promptFile: "./prompt.txt" }));
|
|
60
54
|
assert.throws(
|
|
61
55
|
() => validateOptionCombination({ ...base, prompt: "x", promptFile: "./prompt.txt" }),
|
|
62
|
-
/
|
|
63
|
-
);
|
|
64
|
-
assert.throws(
|
|
65
|
-
() => validateOptionCombination({ ...base, prompt: "x", instructions: "Extra" }),
|
|
66
|
-
/--instructions is only supported in auto mode\./
|
|
56
|
+
/Use exactly one of --prompt or --prompt-file\./
|
|
67
57
|
);
|
|
68
58
|
});
|
|
69
59
|
|
|
70
|
-
test("validateOptionCombination rejects prompt flags
|
|
60
|
+
test("validateOptionCombination rejects --instructions with prompt flags", () => {
|
|
71
61
|
const base: CliOptions = {
|
|
72
|
-
model: "gpt-4o-mini"
|
|
73
|
-
mode: "auto"
|
|
62
|
+
model: "gpt-4o-mini"
|
|
74
63
|
};
|
|
75
64
|
|
|
76
|
-
assert.doesNotThrow(() => validateOptionCombination(base));
|
|
77
65
|
assert.throws(
|
|
78
|
-
() => validateOptionCombination({ ...base, prompt: "
|
|
79
|
-
/--
|
|
66
|
+
() => validateOptionCombination({ ...base, prompt: "x", instructions: "Extra" }),
|
|
67
|
+
/--instructions cannot be combined with --prompt or --prompt-file\./
|
|
80
68
|
);
|
|
81
69
|
assert.throws(
|
|
82
|
-
() => validateOptionCombination({ ...base, promptFile: "./prompt.txt" }),
|
|
83
|
-
/--
|
|
70
|
+
() => validateOptionCombination({ ...base, promptFile: "./prompt.txt", instructions: "Extra" }),
|
|
71
|
+
/--instructions cannot be combined with --prompt or --prompt-file\./
|
|
84
72
|
);
|
|
85
73
|
});
|
|
86
74
|
|