@markbrutx/promptbook-cli 0.1.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/bin/promptbook.ts +4 -0
  4. package/dist/bin/promptbook.d.ts +3 -0
  5. package/dist/bin/promptbook.d.ts.map +1 -0
  6. package/dist/bin/promptbook.js +4 -0
  7. package/dist/bin/promptbook.js.map +1 -0
  8. package/dist/src/args.d.ts +43 -0
  9. package/dist/src/args.d.ts.map +1 -0
  10. package/dist/src/args.js +96 -0
  11. package/dist/src/args.js.map +1 -0
  12. package/dist/src/commands/annotations.d.ts +10 -0
  13. package/dist/src/commands/annotations.d.ts.map +1 -0
  14. package/dist/src/commands/annotations.js +92 -0
  15. package/dist/src/commands/annotations.js.map +1 -0
  16. package/dist/src/commands/bundle.d.ts +13 -0
  17. package/dist/src/commands/bundle.d.ts.map +1 -0
  18. package/dist/src/commands/bundle.js +61 -0
  19. package/dist/src/commands/bundle.js.map +1 -0
  20. package/dist/src/commands/eval.d.ts +13 -0
  21. package/dist/src/commands/eval.d.ts.map +1 -0
  22. package/dist/src/commands/eval.js +113 -0
  23. package/dist/src/commands/eval.js.map +1 -0
  24. package/dist/src/commands/lint.d.ts +11 -0
  25. package/dist/src/commands/lint.d.ts.map +1 -0
  26. package/dist/src/commands/lint.js +66 -0
  27. package/dist/src/commands/lint.js.map +1 -0
  28. package/dist/src/commands/ls.d.ts +11 -0
  29. package/dist/src/commands/ls.d.ts.map +1 -0
  30. package/dist/src/commands/ls.js +84 -0
  31. package/dist/src/commands/ls.js.map +1 -0
  32. package/dist/src/commands/resolve.d.ts +9 -0
  33. package/dist/src/commands/resolve.d.ts.map +1 -0
  34. package/dist/src/commands/resolve.js +41 -0
  35. package/dist/src/commands/resolve.js.map +1 -0
  36. package/dist/src/commands/view.d.ts +30 -0
  37. package/dist/src/commands/view.d.ts.map +1 -0
  38. package/dist/src/commands/view.js +51 -0
  39. package/dist/src/commands/view.js.map +1 -0
  40. package/dist/src/config.d.ts +56 -0
  41. package/dist/src/config.d.ts.map +1 -0
  42. package/dist/src/config.js +175 -0
  43. package/dist/src/config.js.map +1 -0
  44. package/dist/src/index.d.ts +4 -0
  45. package/dist/src/index.d.ts.map +1 -0
  46. package/dist/src/index.js +2 -0
  47. package/dist/src/index.js.map +1 -0
  48. package/dist/src/io.d.ts +43 -0
  49. package/dist/src/io.d.ts.map +1 -0
  50. package/dist/src/io.js +37 -0
  51. package/dist/src/io.js.map +1 -0
  52. package/dist/src/render-eval.d.ts +8 -0
  53. package/dist/src/render-eval.d.ts.map +1 -0
  54. package/dist/src/render-eval.js +51 -0
  55. package/dist/src/render-eval.js.map +1 -0
  56. package/dist/src/render-explain.d.ts +8 -0
  57. package/dist/src/render-explain.d.ts.map +1 -0
  58. package/dist/src/render-explain.js +57 -0
  59. package/dist/src/render-explain.js.map +1 -0
  60. package/dist/src/render-lint.d.ts +8 -0
  61. package/dist/src/render-lint.d.ts.map +1 -0
  62. package/dist/src/render-lint.js +57 -0
  63. package/dist/src/render-lint.js.map +1 -0
  64. package/dist/src/run.d.ts +8 -0
  65. package/dist/src/run.d.ts.map +1 -0
  66. package/dist/src/run.js +105 -0
  67. package/dist/src/run.js.map +1 -0
  68. package/dist/src/style.d.ts +16 -0
  69. package/dist/src/style.d.ts.map +1 -0
  70. package/dist/src/style.js +22 -0
  71. package/dist/src/style.js.map +1 -0
  72. package/package.json +50 -0
  73. package/src/args.ts +145 -0
  74. package/src/commands/annotations.ts +107 -0
  75. package/src/commands/bundle.ts +71 -0
  76. package/src/commands/eval.ts +137 -0
  77. package/src/commands/lint.ts +71 -0
  78. package/src/commands/ls.ts +90 -0
  79. package/src/commands/resolve.ts +47 -0
  80. package/src/commands/view.ts +82 -0
  81. package/src/config.ts +209 -0
  82. package/src/index.ts +3 -0
  83. package/src/io.ts +77 -0
  84. package/src/render-eval.ts +55 -0
  85. package/src/render-explain.ts +64 -0
  86. package/src/render-lint.ts +63 -0
  87. package/src/run.ts +107 -0
  88. package/src/style.ts +37 -0
@@ -0,0 +1,66 @@
1
+ import { defaultRules, lint, loadPrompts, resolveBook } from "@markbrutx/promptbook-core";
2
+ import { buildContext, lintConfigFrom, loadConfig, requirePromptsDir } from "../config.js";
3
+ import { colorEnabled, emitWarnings } from "../io.js";
4
+ import { renderLintReport } from "../render-lint.js";
5
+ /**
6
+ * `lint [<prompt>]`: run static checks over the prompts folder. With a prompt
7
+ * it resolves under the given context and runs both book- and resolved-scope
8
+ * rules; without one it runs book-scope rules over the whole book. The report
9
+ * (human-readable or `--json`) goes to stdout; warnings go to stderr. Exit is
10
+ * non-zero when any error is found, or any warning under `--strict`.
11
+ */
12
+ export async function cmdLint(args, io) {
13
+ const config = await loadConfig(io);
14
+ const promptsDir = await requirePromptsDir(io, args.dir, config);
15
+ if (promptsDir === null) {
16
+ return 1;
17
+ }
18
+ const lintConfig = lintConfigFrom(config);
19
+ const ruleOptions = {};
20
+ const maxTokens = args.maxTokens ?? lintConfig.maxTokens;
21
+ if (maxTokens !== undefined) {
22
+ ruleOptions.maxTokens = maxTokens;
23
+ }
24
+ if (lintConfig.bannedTokens !== undefined) {
25
+ ruleOptions.bannedTokens = lintConfig.bannedTokens;
26
+ }
27
+ const rules = defaultRules(ruleOptions);
28
+ const book = await loadPrompts(promptsDir, io.fs);
29
+ const prompt = args.operands[0];
30
+ let report;
31
+ let label;
32
+ let warnings = book.warnings;
33
+ try {
34
+ if (prompt !== undefined) {
35
+ const context = await buildContext(io, args.ctx, args.contextFile);
36
+ const result = resolveBook(book, prompt, context);
37
+ // trace.warnings already includes the load-time book.warnings.
38
+ warnings = result.trace.warnings;
39
+ report = lint({ book, result }, rules);
40
+ label = prompt;
41
+ }
42
+ else {
43
+ report = lint({ book }, rules);
44
+ label = "book";
45
+ }
46
+ }
47
+ catch (error) {
48
+ io.stderr(`error: ${error.message}\n`);
49
+ return 1;
50
+ }
51
+ emitWarnings(io, warnings);
52
+ if (args.json) {
53
+ io.stdout(`${JSON.stringify(report, null, 2)}\n`);
54
+ }
55
+ else {
56
+ io.stdout(renderLintReport(report, label, colorEnabled(io)));
57
+ }
58
+ if (report.errorCount > 0) {
59
+ return 1;
60
+ }
61
+ if (args.strict && report.warningCount > 0) {
62
+ return 1;
63
+ }
64
+ return 0;
65
+ }
66
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../../src/commands/lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ParsedArgs } from "../args.js";
2
+ import { type IO } from "../io.js";
3
+ /**
4
+ * `ls`: list compositions (name, base length, rule count), code-prompts
5
+ * (name, description, sample count) and fragments (id, kind, tags, source) —
6
+ * the unified menu of every prompt in the book. `--fragments`/`--compositions`
7
+ * narrow the output (code-prompts ride with compositions, both being prompts);
8
+ * `--json` emits a structured list instead of the human-readable tree.
9
+ */
10
+ export declare function cmdLs(args: ParsedArgs, io: IO): Promise<number>;
11
+ //# sourceMappingURL=ls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../../src/commands/ls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAgB,KAAK,EAAE,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4ErE"}
@@ -0,0 +1,84 @@
1
+ import { relative } from "node:path";
2
+ import { loadPrompts } from "@markbrutx/promptbook-core";
3
+ import { requirePromptsDir } from "../config.js";
4
+ import { emitWarnings } from "../io.js";
5
+ /**
6
+ * `ls`: list compositions (name, base length, rule count), code-prompts
7
+ * (name, description, sample count) and fragments (id, kind, tags, source) —
8
+ * the unified menu of every prompt in the book. `--fragments`/`--compositions`
9
+ * narrow the output (code-prompts ride with compositions, both being prompts);
10
+ * `--json` emits a structured list instead of the human-readable tree.
11
+ */
12
+ export async function cmdLs(args, io) {
13
+ const promptsDir = await requirePromptsDir(io, args.dir);
14
+ if (promptsDir === null) {
15
+ return 1;
16
+ }
17
+ const book = await loadPrompts(promptsDir, io.fs);
18
+ emitWarnings(io, book.warnings);
19
+ const onlyOneSection = args.fragments || args.compositions;
20
+ const showCompositions = args.compositions || !onlyOneSection;
21
+ const showFragments = args.fragments || !onlyOneSection;
22
+ const compositions = [...book.compositions.values()].sort((a, b) => a.name.localeCompare(b.name));
23
+ const codePrompts = [...book.codePrompts.values()].sort((a, b) => a.name.localeCompare(b.name));
24
+ const fragments = [...book.fragments.values()].sort((a, b) => a.id.localeCompare(b.id));
25
+ if (args.json) {
26
+ const out = {};
27
+ if (showCompositions) {
28
+ out.compositions = compositions.map((c) => ({
29
+ name: c.name,
30
+ base: c.base.length,
31
+ rules: c.rules.length,
32
+ }));
33
+ out.codePrompts = codePrompts.map((c) => ({
34
+ name: c.name,
35
+ description: c.description ?? null,
36
+ samples: c.samples.length,
37
+ }));
38
+ }
39
+ if (showFragments) {
40
+ out.fragments = fragments.map((f) => ({
41
+ id: f.id,
42
+ kind: f.kind ?? null,
43
+ tags: f.tags ?? [],
44
+ sourceFile: f.sourceFile,
45
+ }));
46
+ }
47
+ io.stdout(`${JSON.stringify(out, null, 2)}\n`);
48
+ return 0;
49
+ }
50
+ const lines = [];
51
+ if (showCompositions) {
52
+ lines.push("compositions:");
53
+ if (compositions.length === 0) {
54
+ lines.push(" (none)");
55
+ }
56
+ for (const c of compositions) {
57
+ lines.push(` ${c.name} base=${c.base.length} rules=${c.rules.length}`);
58
+ }
59
+ if (codePrompts.length > 0) {
60
+ lines.push("");
61
+ lines.push("code-prompts:");
62
+ for (const c of codePrompts) {
63
+ lines.push(` ${c.name} kind=code samples=${c.samples.length}`);
64
+ }
65
+ }
66
+ }
67
+ if (showFragments) {
68
+ if (lines.length > 0) {
69
+ lines.push("");
70
+ }
71
+ lines.push("fragments:");
72
+ if (fragments.length === 0) {
73
+ lines.push(" (none)");
74
+ }
75
+ for (const f of fragments) {
76
+ const tags = f.tags && f.tags.length > 0 ? f.tags.join(",") : "-";
77
+ const source = relative(promptsDir, f.sourceFile) || f.sourceFile;
78
+ lines.push(` ${f.id} kind=${f.kind ?? "-"} tags=${tags} ${source}`);
79
+ }
80
+ }
81
+ io.stdout(`${lines.join("\n")}\n`);
82
+ return 0;
83
+ }
84
+ //# sourceMappingURL=ls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ls.js","sourceRoot":"","sources":["../../../src/commands/ls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ParsedArgs } from "../args.js";
2
+ import { type IO } from "../io.js";
3
+ /**
4
+ * `resolve <prompt>`: assemble the prompt and print its text to stdout. With
5
+ * `--json`, print `{ text, trace }` instead; with `--explain`, additionally
6
+ * print the trace to stderr. Warnings always go to stderr so nothing is lost.
7
+ */
8
+ export declare function cmdResolve(args: ParsedArgs, io: IO): Promise<number>;
9
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/commands/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAA8B,KAAK,EAAE,EAAE,MAAM,UAAU,CAAC;AAG/D;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1E"}
@@ -0,0 +1,41 @@
1
+ import { resolve } from "@markbrutx/promptbook-core";
2
+ import { buildContext, requirePromptsDir } from "../config.js";
3
+ import { colorEnabled, emitWarnings } from "../io.js";
4
+ import { renderExplain } from "../render-explain.js";
5
+ /**
6
+ * `resolve <prompt>`: assemble the prompt and print its text to stdout. With
7
+ * `--json`, print `{ text, trace }` instead; with `--explain`, additionally
8
+ * print the trace to stderr. Warnings always go to stderr so nothing is lost.
9
+ */
10
+ export async function cmdResolve(args, io) {
11
+ const prompt = args.operands[0];
12
+ if (prompt === undefined) {
13
+ io.stderr('error: resolve requires a <prompt> name. Run "promptbook --help".\n');
14
+ return 1;
15
+ }
16
+ const promptsDir = await requirePromptsDir(io, args.dir);
17
+ if (promptsDir === null) {
18
+ return 1;
19
+ }
20
+ let result;
21
+ try {
22
+ const context = await buildContext(io, args.ctx, args.contextFile);
23
+ result = await resolve({ promptsDir, prompt, context, fs: io.fs });
24
+ }
25
+ catch (error) {
26
+ io.stderr(`error: ${error.message}\n`);
27
+ return 1;
28
+ }
29
+ const { text, trace } = result;
30
+ emitWarnings(io, trace.warnings);
31
+ if (args.json) {
32
+ io.stdout(`${JSON.stringify({ text, trace }, null, 2)}\n`);
33
+ return 0;
34
+ }
35
+ if (args.explain) {
36
+ io.stderr(renderExplain(trace, colorEnabled(io)));
37
+ }
38
+ io.stdout(`${text}\n`);
39
+ return 0;
40
+ }
41
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/commands/resolve.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAW,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB,EAAE,EAAM;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,qEAAqE,CAAC,CAAC;QACjF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,MAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,EAAE,CAAC,MAAM,CAAC,UAAW,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEjC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3D,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { ParsedArgs } from "../args.js";
2
+ import type { IO } from "../io.js";
3
+ /** Options forwarded to `@markbrutx/promptbook-viewer`'s `startViewer`. */
4
+ export interface ViewerStartOptions {
5
+ promptsDir: string;
6
+ port?: number;
7
+ open: boolean;
8
+ }
9
+ /** The subset of the viewer's running handle the CLI uses. */
10
+ export interface ViewerHandle {
11
+ url: string;
12
+ close(): Promise<void>;
13
+ }
14
+ /**
15
+ * Injectable seam for `view`: how to start the viewer and how to keep the
16
+ * process alive. Defaults dynamically import the optional `@markbrutx/promptbook-viewer`
17
+ * package and block until a termination signal; tests inject fakes so no
18
+ * server or signal handling is needed.
19
+ */
20
+ export interface ViewDeps {
21
+ start(options: ViewerStartOptions): Promise<ViewerHandle>;
22
+ hold(handle: ViewerHandle): Promise<number>;
23
+ }
24
+ /**
25
+ * `view`: start the local web viewer over the prompts folder, print its URL,
26
+ * and keep running until interrupted. Render-only; the viewer makes no model
27
+ * calls. `--no-open` skips launching the browser; `--port` pins the port.
28
+ */
29
+ export declare function cmdView(args: ParsedArgs, io: IO, deps?: ViewDeps): Promise<number>;
30
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/commands/view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEnC,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AA8BD;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,GAAE,QAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBzG"}
@@ -0,0 +1,51 @@
1
+ import { requirePromptsDir } from "../config.js";
2
+ /** Thrown by the default `start` when the optional viewer package is absent. */
3
+ class ViewerNotInstalledError extends Error {
4
+ }
5
+ const defaultViewDeps = {
6
+ async start(options) {
7
+ let mod;
8
+ try {
9
+ mod = (await import("@markbrutx/promptbook-viewer"));
10
+ }
11
+ catch {
12
+ throw new ViewerNotInstalledError();
13
+ }
14
+ return mod.startViewer(options);
15
+ },
16
+ hold(handle) {
17
+ return new Promise((resolve) => {
18
+ const shutdown = () => {
19
+ void handle.close().then(() => resolve(0));
20
+ };
21
+ process.once("SIGINT", shutdown);
22
+ process.once("SIGTERM", shutdown);
23
+ });
24
+ },
25
+ };
26
+ /**
27
+ * `view`: start the local web viewer over the prompts folder, print its URL,
28
+ * and keep running until interrupted. Render-only; the viewer makes no model
29
+ * calls. `--no-open` skips launching the browser; `--port` pins the port.
30
+ */
31
+ export async function cmdView(args, io, deps = defaultViewDeps) {
32
+ const promptsDir = await requirePromptsDir(io, args.dir);
33
+ if (promptsDir === null) {
34
+ return 1;
35
+ }
36
+ let handle;
37
+ try {
38
+ handle = await deps.start({ promptsDir, port: args.port, open: !args.noOpen });
39
+ }
40
+ catch (error) {
41
+ if (error instanceof ViewerNotInstalledError) {
42
+ io.stderr('error: the viewer is not installed. Add it with "npm i -D @markbrutx/promptbook-viewer".\n');
43
+ return 1;
44
+ }
45
+ io.stderr(`error: ${error.message}\n`);
46
+ return 1;
47
+ }
48
+ io.stdout(`promptbook viewer running at ${handle.url}\n`);
49
+ return deps.hold(handle);
50
+ }
51
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/commands/view.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AA2BjD,gFAAgF;AAChF,MAAM,uBAAwB,SAAQ,KAAK;CAAG;AAM9C,MAAM,eAAe,GAAa;IAChC,KAAK,CAAC,KAAK,CAAC,OAAO;QACjB,IAAI,GAAiB,CAAC;QACtB,IAAI,CAAC;YACH,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAA4B,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,uBAAuB,EAAE,CAAC;QACtC,CAAC;QACD,OAAO,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,MAAM;QACT,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,GAAS,EAAE;gBAC1B,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAgB,EAAE,EAAM,EAAE,IAAI,GAAa,eAAe;IACtF,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,MAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACjF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;YAC7C,EAAE,CAAC,MAAM,CAAC,4FAA4F,CAAC,CAAC;YACxG,OAAO,CAAC,CAAC;QACX,CAAC;QACD,EAAE,CAAC,MAAM,CAAC,UAAW,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,EAAE,CAAC,MAAM,CAAC,gCAAgC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { Context, ContextValue } from "@markbrutx/promptbook-core";
2
+ import type { IO } from "./io.js";
3
+ /**
4
+ * Coerce a raw `--ctx` value to a {@link ContextValue}: `true`/`false` become
5
+ * booleans, integer/decimal literals become numbers, everything else stays a
6
+ * string. For values that must stay strings (e.g. "123"), use `--context-file`.
7
+ */
8
+ export declare function coerceScalar(raw: string): ContextValue;
9
+ /** Parse repeated `key=value` pairs into a context bag, coercing each value. */
10
+ export declare function parseCtxPairs(pairs: string[]): Context;
11
+ /**
12
+ * Build the resolve context: `--context-file` first, then `--ctx` pairs layered
13
+ * on top so explicit flags win over the file.
14
+ */
15
+ export declare function buildContext(io: IO, pairs: string[], contextFile?: string): Promise<Context>;
16
+ interface PromptbookConfig {
17
+ promptsDir?: unknown;
18
+ lint?: unknown;
19
+ eval?: unknown;
20
+ }
21
+ /** lint options sourced from the `lint` section of `promptbook.json`. */
22
+ export interface LintConfig {
23
+ maxTokens?: number;
24
+ bannedTokens?: string[];
25
+ }
26
+ /**
27
+ * Read and parse `promptbook.json` from cwd once. Missing, unreadable or
28
+ * malformed config yields an empty object, so callers treat it as best-effort
29
+ * and layer flags on top. Pass the result to {@link resolvePromptsDir} and
30
+ * {@link lintConfigFrom} to avoid re-reading the file per command.
31
+ */
32
+ export declare function loadConfig(io: IO): Promise<PromptbookConfig>;
33
+ /** Extract the `lint` section from an already-loaded config. */
34
+ export declare function lintConfigFrom(config: PromptbookConfig): LintConfig;
35
+ /** Convenience wrapper: load config and extract its `lint` section. */
36
+ export declare function loadLintConfig(io: IO): Promise<LintConfig>;
37
+ /** eval options sourced from the `eval` section of `promptbook.json`. */
38
+ export interface EvalConfig {
39
+ model?: string;
40
+ baseUrl?: string;
41
+ }
42
+ /** Extract the `eval` section from an already-loaded config. */
43
+ export declare function evalConfigFrom(config: PromptbookConfig): EvalConfig;
44
+ /**
45
+ * Resolve the prompts folder by priority: `--dir` flag > `promptbook.json`
46
+ * (`promptsDir` key) in cwd > `./prompts`. All results are absolute. Pass a
47
+ * preloaded `config` to reuse a single read; otherwise it is loaded here.
48
+ */
49
+ export declare function resolvePromptsDir(io: IO, dirFlag?: string, config?: PromptbookConfig): Promise<string>;
50
+ /**
51
+ * Resolve the prompts folder and confirm it exists. On a missing folder, write
52
+ * a clear error to stderr and return null so the caller can exit non-zero.
53
+ */
54
+ export declare function requirePromptsDir(io: IO, dirFlag?: string, config?: PromptbookConfig): Promise<string | null>;
55
+ export {};
56
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AASlC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAWtD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CActD;AAuBD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAalG;AAED,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,yEAAyE;AACzE,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAclE;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAanE;AAED,uEAAuE;AACvE,wBAAsB,cAAc,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAEhE;AAED,yEAAyE;AACzE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAanE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,EAAE,EACN,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CASjB;AAYD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,EAAE,EACN,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
@@ -0,0 +1,175 @@
1
+ import { resolve as resolvePath } from "node:path";
2
+ const NUMERIC = /^-?\d+(?:\.\d+)?$/;
3
+ /** True for a plain JSON object: an object that is neither null nor an array. */
4
+ function isJsonObject(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ /**
8
+ * Coerce a raw `--ctx` value to a {@link ContextValue}: `true`/`false` become
9
+ * booleans, integer/decimal literals become numbers, everything else stays a
10
+ * string. For values that must stay strings (e.g. "123"), use `--context-file`.
11
+ */
12
+ export function coerceScalar(raw) {
13
+ if (raw === "true") {
14
+ return true;
15
+ }
16
+ if (raw === "false") {
17
+ return false;
18
+ }
19
+ if (NUMERIC.test(raw)) {
20
+ return Number(raw);
21
+ }
22
+ return raw;
23
+ }
24
+ /** Parse repeated `key=value` pairs into a context bag, coercing each value. */
25
+ export function parseCtxPairs(pairs) {
26
+ const context = {};
27
+ for (const pair of pairs) {
28
+ const eq = pair.indexOf("=");
29
+ if (eq === -1) {
30
+ throw new Error(`invalid --ctx "${pair}"; expected key=value`);
31
+ }
32
+ const key = pair.slice(0, eq);
33
+ if (key === "") {
34
+ throw new Error(`invalid --ctx "${pair}"; key is empty`);
35
+ }
36
+ context[key] = coerceScalar(pair.slice(eq + 1));
37
+ }
38
+ return context;
39
+ }
40
+ function parseContextFile(raw, path) {
41
+ let data;
42
+ try {
43
+ data = JSON.parse(raw);
44
+ }
45
+ catch (error) {
46
+ throw new Error(`context file "${path}" is not valid JSON: ${error.message}`);
47
+ }
48
+ if (!isJsonObject(data)) {
49
+ throw new Error(`context file "${path}" must be a JSON object`);
50
+ }
51
+ const context = {};
52
+ for (const [key, value] of Object.entries(data)) {
53
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
54
+ context[key] = value;
55
+ }
56
+ else {
57
+ throw new Error(`context file "${path}" key "${key}" must be a string, number or boolean`);
58
+ }
59
+ }
60
+ return context;
61
+ }
62
+ /**
63
+ * Build the resolve context: `--context-file` first, then `--ctx` pairs layered
64
+ * on top so explicit flags win over the file.
65
+ */
66
+ export async function buildContext(io, pairs, contextFile) {
67
+ let fileContext = {};
68
+ if (contextFile !== undefined) {
69
+ const path = resolvePath(io.cwd(), contextFile);
70
+ let raw;
71
+ try {
72
+ raw = await io.fs.readFile(path);
73
+ }
74
+ catch {
75
+ throw new Error(`context file not found: ${path}`);
76
+ }
77
+ fileContext = parseContextFile(raw, path);
78
+ }
79
+ return { ...fileContext, ...parseCtxPairs(pairs) };
80
+ }
81
+ /**
82
+ * Read and parse `promptbook.json` from cwd once. Missing, unreadable or
83
+ * malformed config yields an empty object, so callers treat it as best-effort
84
+ * and layer flags on top. Pass the result to {@link resolvePromptsDir} and
85
+ * {@link lintConfigFrom} to avoid re-reading the file per command.
86
+ */
87
+ export async function loadConfig(io) {
88
+ const configPath = resolvePath(io.cwd(), "promptbook.json");
89
+ let raw;
90
+ try {
91
+ raw = await io.fs.readFile(configPath);
92
+ }
93
+ catch {
94
+ return {};
95
+ }
96
+ try {
97
+ const parsed = JSON.parse(raw);
98
+ return isJsonObject(parsed) ? parsed : {};
99
+ }
100
+ catch {
101
+ return {};
102
+ }
103
+ }
104
+ /** Extract the `lint` section from an already-loaded config. */
105
+ export function lintConfigFrom(config) {
106
+ const section = config.lint;
107
+ if (!isJsonObject(section)) {
108
+ return {};
109
+ }
110
+ const lint = {};
111
+ if (typeof section.maxTokens === "number") {
112
+ lint.maxTokens = section.maxTokens;
113
+ }
114
+ if (Array.isArray(section.bannedTokens)) {
115
+ lint.bannedTokens = section.bannedTokens.filter((token) => typeof token === "string");
116
+ }
117
+ return lint;
118
+ }
119
+ /** Convenience wrapper: load config and extract its `lint` section. */
120
+ export async function loadLintConfig(io) {
121
+ return lintConfigFrom(await loadConfig(io));
122
+ }
123
+ /** Extract the `eval` section from an already-loaded config. */
124
+ export function evalConfigFrom(config) {
125
+ const section = config.eval;
126
+ if (!isJsonObject(section)) {
127
+ return {};
128
+ }
129
+ const evalConfig = {};
130
+ if (typeof section.model === "string") {
131
+ evalConfig.model = section.model;
132
+ }
133
+ if (typeof section.baseUrl === "string") {
134
+ evalConfig.baseUrl = section.baseUrl;
135
+ }
136
+ return evalConfig;
137
+ }
138
+ /**
139
+ * Resolve the prompts folder by priority: `--dir` flag > `promptbook.json`
140
+ * (`promptsDir` key) in cwd > `./prompts`. All results are absolute. Pass a
141
+ * preloaded `config` to reuse a single read; otherwise it is loaded here.
142
+ */
143
+ export async function resolvePromptsDir(io, dirFlag, config) {
144
+ if (dirFlag !== undefined) {
145
+ return resolvePath(io.cwd(), dirFlag);
146
+ }
147
+ const resolved = config ?? (await loadConfig(io));
148
+ if (typeof resolved.promptsDir === "string") {
149
+ return resolvePath(io.cwd(), resolved.promptsDir);
150
+ }
151
+ return resolvePath(io.cwd(), "prompts");
152
+ }
153
+ /** Whether a directory can be listed; used to give a clear missing-folder error. */
154
+ async function dirExists(io, dir) {
155
+ try {
156
+ await io.fs.readDir(dir);
157
+ return true;
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Resolve the prompts folder and confirm it exists. On a missing folder, write
165
+ * a clear error to stderr and return null so the caller can exit non-zero.
166
+ */
167
+ export async function requirePromptsDir(io, dirFlag, config) {
168
+ const promptsDir = await resolvePromptsDir(io, dirFlag, config);
169
+ if (!(await dirExists(io, promptsDir))) {
170
+ io.stderr(`error: prompts folder not found: ${promptsDir}\n`);
171
+ return null;
172
+ }
173
+ return promptsDir;
174
+ }
175
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type { ParsedArgs } from "./args.js";
2
+ export type { IO } from "./io.js";
3
+ export { run } from "./run.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,YAAY,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { run } from "./run.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { FsAdapter, ModelAdapter } from "@markbrutx/promptbook-core";
2
+ /** Options the `eval` command needs to build a model adapter. */
3
+ export interface AdapterOptions {
4
+ model?: string;
5
+ apiKey?: string;
6
+ baseUrl?: string;
7
+ }
8
+ /**
9
+ * Injectable side-effect surface for the CLI.
10
+ *
11
+ * Keeping stdout/stderr/cwd/fs behind this interface lets {@link run} be driven
12
+ * from tests without spawning a process. The stream contract is fixed:
13
+ * **stdout = payload** (prompt text or JSON), **stderr = explanations/errors**.
14
+ */
15
+ export interface IO {
16
+ /** Write payload bytes verbatim (the command appends its own newlines). */
17
+ stdout(text: string): void;
18
+ /** Write explanations, warnings and errors verbatim. */
19
+ stderr(text: string): void;
20
+ /** Write a file to disk, creating parent directories as needed (e.g. `bundle -o`). */
21
+ writeFile(path: string, contents: string): Promise<void>;
22
+ /** Current working directory, used to resolve relative paths and config. */
23
+ cwd(): string;
24
+ /** Environment bag, consulted for `NO_COLOR`. */
25
+ env: Record<string, string | undefined>;
26
+ /** Filesystem adapter passed straight through to the core. */
27
+ fs: FsAdapter;
28
+ /** Whether color is allowed when `NO_COLOR` is unset (true on a TTY). */
29
+ colorDefault: boolean;
30
+ /**
31
+ * Build the model adapter for `eval`. Tests inject a fake so no network or
32
+ * key is needed; when unset, the command falls back to the OpenRouter
33
+ * adapter built from flags/config/env.
34
+ */
35
+ makeAdapter?(options: AdapterOptions): ModelAdapter;
36
+ }
37
+ /** Real-process IO: stdout/stderr streams, Node fs, live env and cwd. */
38
+ export declare function defaultIO(): IO;
39
+ /** Emit each warning to stderr with the standard `warning:` prefix. */
40
+ export declare function emitWarnings(io: IO, warnings: string[]): void;
41
+ /** Resolve whether colored output is allowed: off if `NO_COLOR` is set. */
42
+ export declare function colorEnabled(io: IO): boolean;
43
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/io.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1E,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,EAAE;IACjB,2EAA2E;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wDAAwD;IACxD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,sFAAsF;IACtF,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,4EAA4E;IAC5E,GAAG,IAAI,MAAM,CAAC;IACd,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,8DAA8D;IAC9D,EAAE,EAAE,SAAS,CAAC;IACd,yEAAyE;IACzE,YAAY,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,CAAC;CACrD;AAED,yEAAyE;AACzE,wBAAgB,SAAS,IAAI,EAAE,CAiB9B;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAI7D;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAM5C"}