@hivecommons/promptargs 0.7.0 → 0.8.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 +100 -15
- package/dist/autodetect.js +24 -9
- package/dist/autodetect.js.map +1 -1
- package/dist/cli.js +51 -147
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +130 -0
- package/dist/commands.js.map +1 -0
- package/dist/flags.d.ts +14 -0
- package/dist/flags.js +57 -0
- package/dist/flags.js.map +1 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +64 -11
- package/dist/loader.js.map +1 -1
- package/dist/log.d.ts +19 -0
- package/dist/log.js +41 -0
- package/dist/log.js.map +1 -0
- package/dist/parser.js +33 -7
- package/dist/parser.js.map +1 -1
- package/dist/resolver.d.ts +2 -0
- package/dist/resolver.js +60 -30
- package/dist/resolver.js.map +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/status.js +5 -4
- package/dist/status.js.map +1 -1
- package/dist/ui.html +18 -5
- package/dist/ui.js +36 -11
- package/dist/ui.js.map +1 -1
- package/package.json +13 -4
- package/dist/autodetect.test.d.ts +0 -1
- package/dist/autodetect.test.js +0 -207
- package/dist/autodetect.test.js.map +0 -1
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -310
- package/dist/cli.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -1
- package/dist/index.test.js +0 -59
- package/dist/index.test.js.map +0 -1
- package/dist/iterate.test.d.ts +0 -1
- package/dist/iterate.test.js +0 -20
- package/dist/iterate.test.js.map +0 -1
- package/dist/loader.test.d.ts +0 -1
- package/dist/loader.test.js +0 -65
- package/dist/loader.test.js.map +0 -1
- package/dist/parser.test.d.ts +0 -1
- package/dist/parser.test.js +0 -93
- package/dist/parser.test.js.map +0 -1
- package/dist/resolver.test.d.ts +0 -1
- package/dist/resolver.test.js +0 -184
- package/dist/resolver.test.js.map +0 -1
- package/dist/sanitize.test.d.ts +0 -1
- package/dist/sanitize.test.js +0 -38
- package/dist/sanitize.test.js.map +0 -1
- package/dist/status.test.d.ts +0 -1
- package/dist/status.test.js +0 -33
- package/dist/status.test.js.map +0 -1
- package/dist/ui-bootstrap.test.d.ts +0 -1
- package/dist/ui-bootstrap.test.js +0 -479
- package/dist/ui-bootstrap.test.js.map +0 -1
- package/dist/ui-builder.test.d.ts +0 -1
- package/dist/ui-builder.test.js +0 -272
- package/dist/ui-builder.test.js.map +0 -1
- package/dist/ui-command-quoting.test.d.ts +0 -1
- package/dist/ui-command-quoting.test.js +0 -95
- package/dist/ui-command-quoting.test.js.map +0 -1
- package/dist/ui-harness.d.ts +0 -20
- package/dist/ui-harness.js +0 -40
- package/dist/ui-harness.js.map +0 -1
- package/dist/ui-harness.test.d.ts +0 -1
- package/dist/ui-harness.test.js +0 -30
- package/dist/ui-harness.test.js.map +0 -1
- package/dist/ui-inline.test.d.ts +0 -1
- package/dist/ui-inline.test.js +0 -90
- package/dist/ui-inline.test.js.map +0 -1
- package/dist/ui-interactions.test.d.ts +0 -1
- package/dist/ui-interactions.test.js +0 -489
- package/dist/ui-interactions.test.js.map +0 -1
- package/dist/ui.test.d.ts +0 -1
- package/dist/ui.test.js +0 -283
- package/dist/ui.test.js.map +0 -1
package/dist/commands.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { parseVars, VAR_PATTERN } from './parser.js';
|
|
4
|
+
import { findTemplate, loadTemplates } from './loader.js';
|
|
5
|
+
import { autodetect, AUTODETECT_VARS } from './autodetect.js';
|
|
6
|
+
import { sanitizeForTerminal } from './sanitize.js';
|
|
7
|
+
import { UsageError, assertNoReservedVars, subcommandCollision } from './flags.js';
|
|
8
|
+
const EXAMPLE_REVIEW = `Review {{file}} for {{focus=correctness}} issues.
|
|
9
|
+
Be {{tone=concise}} in your feedback.
|
|
10
|
+
Focus on real bugs, not style nitpicks.
|
|
11
|
+
`;
|
|
12
|
+
const EXAMPLE_EXPLAIN = `Explain what {{file}} does in {{style=simple}} terms.
|
|
13
|
+
Assume the reader is a {{audience=junior developer}}.
|
|
14
|
+
`;
|
|
15
|
+
const EXAMPLE_FIX = `Fix the {{issue}} in {{file}}.
|
|
16
|
+
The expected behavior is: {{expected}}
|
|
17
|
+
The actual behavior is: {{actual}}
|
|
18
|
+
`;
|
|
19
|
+
export function doInit() {
|
|
20
|
+
const dir = join(process.cwd(), '.prompts');
|
|
21
|
+
if (existsSync(dir)) {
|
|
22
|
+
console.log('.prompts/ already exists!');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, 'review.md'), EXAMPLE_REVIEW);
|
|
27
|
+
writeFileSync(join(dir, 'explain.md'), EXAMPLE_EXPLAIN);
|
|
28
|
+
writeFileSync(join(dir, 'fix.md'), EXAMPLE_FIX);
|
|
29
|
+
console.log('Created .prompts/ with 3 example templates:');
|
|
30
|
+
console.log(' review.md — Code review with focus area');
|
|
31
|
+
console.log(' explain.md — Explain code simply');
|
|
32
|
+
console.log(' fix.md — Bug fix template');
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log('Try it: promptargs review --file=src/main.ts');
|
|
35
|
+
}
|
|
36
|
+
export function doList() {
|
|
37
|
+
const templates = loadTemplates();
|
|
38
|
+
if (templates.length === 0) {
|
|
39
|
+
console.log('No templates found.');
|
|
40
|
+
console.log('Run "promptargs init" to create example templates.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log('Available templates:\n');
|
|
44
|
+
for (const t of templates) {
|
|
45
|
+
const source = t.source === 'user' ? ' (user)' : '';
|
|
46
|
+
console.log(sanitizeForTerminal(` ${t.name}${source}`));
|
|
47
|
+
if (t.error) {
|
|
48
|
+
console.log(sanitizeForTerminal(` vars: (error: could not be read: ${t.error})`));
|
|
49
|
+
console.log('');
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const collision = subcommandCollision(t.name);
|
|
53
|
+
if (collision) {
|
|
54
|
+
console.log(sanitizeForTerminal(` ${collision}`));
|
|
55
|
+
console.log('');
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const tplVars = parseVars(t.content);
|
|
60
|
+
assertNoReservedVars(tplVars);
|
|
61
|
+
const varNames = tplVars.map(v => {
|
|
62
|
+
if (v.defaultValue !== undefined)
|
|
63
|
+
return `${v.name}=${v.defaultValue}`;
|
|
64
|
+
return v.name;
|
|
65
|
+
});
|
|
66
|
+
if (varNames.length === 0) {
|
|
67
|
+
console.log(sanitizeForTerminal(` vars: (none)`));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(sanitizeForTerminal(` vars: {{${varNames.join('}} {{')}}}`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.log(sanitizeForTerminal(` vars: (error: ${err instanceof Error ? err.message : String(err)})`));
|
|
75
|
+
}
|
|
76
|
+
console.log('');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function doShow(templateName) {
|
|
80
|
+
if (!templateName) {
|
|
81
|
+
throw new UsageError('Usage: promptargs show <template>');
|
|
82
|
+
}
|
|
83
|
+
const tpl = findTemplate(templateName);
|
|
84
|
+
if (!tpl) {
|
|
85
|
+
throw new UsageError(`Template "${templateName}" not found.`);
|
|
86
|
+
}
|
|
87
|
+
const collision = subcommandCollision(tpl.name);
|
|
88
|
+
if (collision)
|
|
89
|
+
console.error(sanitizeForTerminal(collision));
|
|
90
|
+
// Defaults are merged across repeated blanks, so {{name}} … {{name=dflt}}
|
|
91
|
+
// must colour every occurrence as "has default", not just the one that
|
|
92
|
+
// carries it.
|
|
93
|
+
const tplVars = parseVars(tpl.content);
|
|
94
|
+
assertNoReservedVars(tplVars);
|
|
95
|
+
const hasDefault = new Set(tplVars
|
|
96
|
+
.filter((v) => v.defaultValue !== undefined)
|
|
97
|
+
.map((v) => v.name));
|
|
98
|
+
// Sanitize first: templates are repo-controlled and must not be able to
|
|
99
|
+
// inject their own terminal escapes; only our highlighting below may.
|
|
100
|
+
const highlighted = sanitizeForTerminal(tpl.content).replace(VAR_PATTERN, (raw, varName) => {
|
|
101
|
+
// Colour the tag exactly as written so padded tags ({{ name }}) are
|
|
102
|
+
// shown as the author typed them.
|
|
103
|
+
if (hasDefault.has(varName)) {
|
|
104
|
+
return `\x1b[33m${raw}\x1b[0m`;
|
|
105
|
+
}
|
|
106
|
+
return `\x1b[31m${raw}\x1b[0m`;
|
|
107
|
+
});
|
|
108
|
+
console.log(sanitizeForTerminal(`Template: ${tpl.name} (${tpl.path})`) + '\n');
|
|
109
|
+
console.log(highlighted);
|
|
110
|
+
console.log('\n\x1b[31mred\x1b[0m = required \x1b[33myellow\x1b[0m = has default');
|
|
111
|
+
}
|
|
112
|
+
const DIFF_PREVIEW_MAX_CHARS = 60;
|
|
113
|
+
export function printEnv() {
|
|
114
|
+
console.log('Auto-detected variables:\n');
|
|
115
|
+
for (const name of AUTODETECT_VARS) {
|
|
116
|
+
const value = autodetect(name);
|
|
117
|
+
if (value !== undefined) {
|
|
118
|
+
let display = value;
|
|
119
|
+
if (name === 'diff' && display.length > DIFF_PREVIEW_MAX_CHARS) {
|
|
120
|
+
display = display.slice(0, DIFF_PREVIEW_MAX_CHARS) + '...';
|
|
121
|
+
}
|
|
122
|
+
console.log(` \x1b[32m{{${name}}}\x1b[0m = ${sanitizeForTerminal(display)}`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(` \x1b[90m{{${name}}}\x1b[0m = (not detected)`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log('');
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/flags.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class UsageError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare const SWITCHES: Set<string>;
|
|
5
|
+
declare const SUBCOMMAND_NAMES: readonly ["help", "env", "ui", "init", "list", "show"];
|
|
6
|
+
export type Subcommand = (typeof SUBCOMMAND_NAMES)[number];
|
|
7
|
+
export declare const SUBCOMMANDS: Set<string>;
|
|
8
|
+
export declare function subcommandCollision(name: string): string | undefined;
|
|
9
|
+
export declare function assertNoReservedVars(vars: {
|
|
10
|
+
name: string;
|
|
11
|
+
}[]): void;
|
|
12
|
+
export declare function parseFlags(args: string[]): Record<string, string>;
|
|
13
|
+
export declare function parsePort(flags: Record<string, string>): number | undefined;
|
|
14
|
+
export {};
|
package/dist/flags.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class UsageError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'UsageError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export const SWITCHES = new Set(['no-interactive', 'cross', 'json', 'status']);
|
|
8
|
+
const SUBCOMMAND_NAMES = ['help', 'env', 'ui', 'init', 'list', 'show'];
|
|
9
|
+
export const SUBCOMMANDS = new Set(SUBCOMMAND_NAMES);
|
|
10
|
+
export function subcommandCollision(name) {
|
|
11
|
+
return SUBCOMMANDS.has(name)
|
|
12
|
+
? `(unreachable: name collides with the "${name}" subcommand; rename the file)`
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
15
|
+
export function assertNoReservedVars(vars) {
|
|
16
|
+
for (const v of vars) {
|
|
17
|
+
if (SWITCHES.has(v.name)) {
|
|
18
|
+
throw new UsageError(`Unsupported variable name "${v.name}": it is reserved for the --${v.name} switch; rename the blank`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function parseFlags(args) {
|
|
23
|
+
const flags = {};
|
|
24
|
+
for (let i = 0; i < args.length; i++) {
|
|
25
|
+
const arg = args[i];
|
|
26
|
+
if (!arg.startsWith('--')) {
|
|
27
|
+
throw new UsageError(`Unexpected argument: ${arg}`);
|
|
28
|
+
}
|
|
29
|
+
const eq = arg.indexOf('=');
|
|
30
|
+
if (eq > 0) {
|
|
31
|
+
flags[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const name = arg.slice(2);
|
|
35
|
+
if (SWITCHES.has(name)) {
|
|
36
|
+
flags[name] = 'true';
|
|
37
|
+
}
|
|
38
|
+
else if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
39
|
+
flags[name] = args[++i];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new UsageError(`Invalid --${name}: --${name} requires a value: --${name}=<value>`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return flags;
|
|
46
|
+
}
|
|
47
|
+
export function parsePort(flags) {
|
|
48
|
+
if (!('port' in flags))
|
|
49
|
+
return undefined;
|
|
50
|
+
// Number('') is 0, so an empty --port= must be rejected before the range check.
|
|
51
|
+
const port = Number(flags['port']);
|
|
52
|
+
if (flags['port'] === '' || !Number.isInteger(port) || port < 0 || port > 65535) {
|
|
53
|
+
throw new UsageError('Invalid --port: expected an integer from 0 to 65535. Usage: promptargs ui --port=3700');
|
|
54
|
+
}
|
|
55
|
+
return port;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"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"}
|
package/dist/loader.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface PromptTemplate {
|
|
|
6
6
|
content: string;
|
|
7
7
|
path: string;
|
|
8
8
|
source: 'project' | 'user';
|
|
9
|
+
/** Set when the file could not be read; content is then empty. */
|
|
10
|
+
error?: string;
|
|
9
11
|
}
|
|
10
12
|
export declare function loadTemplates(cwd?: string): PromptTemplate[];
|
|
11
13
|
export declare function findTemplate(name: string, cwd?: string): PromptTemplate | undefined;
|
package/dist/loader.js
CHANGED
|
@@ -1,22 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Find and load prompt templates from .prompts/ directories.
|
|
3
3
|
*/
|
|
4
|
-
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
5
|
-
import { join, basename, extname } from 'node:path';
|
|
4
|
+
import { readFileSync, readdirSync, existsSync, statSync, realpathSync } from 'node:fs';
|
|
5
|
+
import { join, basename, extname, sep } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
|
+
import { UsageError } from './flags.js';
|
|
8
|
+
import { diag, classifyError } from './log.js';
|
|
9
|
+
import { sanitizeForTerminal } from './sanitize.js';
|
|
7
10
|
const PROJECT_DIR = '.prompts';
|
|
8
11
|
const USER_DIR = join(homedir(), '.prompts');
|
|
9
12
|
function loadFromDir(dir, source) {
|
|
10
13
|
if (!existsSync(dir))
|
|
11
14
|
return [];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
let entries;
|
|
16
|
+
try {
|
|
17
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21
|
+
diag('loader_dir_unreadable', { error_class: classifyError(err) }, sanitizeForTerminal(`promptargs: cannot read ${dir}: ${msg}`));
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
// Symlinked templates must resolve to a file inside this .prompts dir; a
|
|
25
|
+
// cloned repo must not be able to make `promptargs x` read ~/.ssh/id_rsa.
|
|
26
|
+
let realDir;
|
|
27
|
+
try {
|
|
28
|
+
realDir = realpathSync(dir);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32
|
+
diag('loader_dir_unreadable', { error_class: classifyError(err) }, sanitizeForTerminal(`promptargs: cannot resolve ${dir}: ${msg}`));
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const realDirPrefix = realDir.endsWith(sep) ? realDir : realDir + sep;
|
|
36
|
+
const templates = [];
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const f = entry.name;
|
|
39
|
+
if (!f.endsWith('.md'))
|
|
40
|
+
continue;
|
|
41
|
+
const path = join(dir, f);
|
|
42
|
+
const isLink = entry.isSymbolicLink();
|
|
43
|
+
try {
|
|
44
|
+
const isFile = isLink ? statSync(path).isFile() : entry.isFile();
|
|
45
|
+
if (!isFile)
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
continue; // dangling symlink
|
|
50
|
+
}
|
|
51
|
+
const template = { name: basename(f, extname(f)), content: '', path, source };
|
|
52
|
+
try {
|
|
53
|
+
if (isLink) {
|
|
54
|
+
const target = realpathSync(path);
|
|
55
|
+
if (!target.startsWith(realDirPrefix)) {
|
|
56
|
+
template.error = `symlink target ${target} is outside ${dir}; templates must stay inside the .prompts directory`;
|
|
57
|
+
templates.push(template);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
template.content = readFileSync(path, 'utf8');
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
template.error = err instanceof Error ? err.message : String(err);
|
|
65
|
+
}
|
|
66
|
+
templates.push(template);
|
|
67
|
+
}
|
|
68
|
+
return templates;
|
|
20
69
|
}
|
|
21
70
|
export function loadTemplates(cwd) {
|
|
22
71
|
const projectDir = join(cwd || process.cwd(), PROJECT_DIR);
|
|
@@ -31,6 +80,10 @@ export function loadTemplates(cwd) {
|
|
|
31
80
|
return Array.from(byName.values());
|
|
32
81
|
}
|
|
33
82
|
export function findTemplate(name, cwd) {
|
|
34
|
-
|
|
83
|
+
const tpl = loadTemplates(cwd).find(t => t.name === name);
|
|
84
|
+
if (tpl?.error) {
|
|
85
|
+
throw new UsageError(`Template "${tpl.name}" at ${tpl.path} could not be read: ${tpl.error}`);
|
|
86
|
+
}
|
|
87
|
+
return tpl;
|
|
35
88
|
}
|
|
36
89
|
//# sourceMappingURL=loader.js.map
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in structured diagnostics. With PROMPTARGS_LOG=json, diagnostics are
|
|
3
|
+
* written to stderr as one JSON object per line with a fixed, bounded field
|
|
4
|
+
* set. Template contents, argument values, paths and URLs are never logged.
|
|
5
|
+
*/
|
|
6
|
+
export type LogEvent = 'ui_host_rejected' | 'ui_host_rejected_total' | 'ui_asset_error' | 'ui_request' | 'ui_start_error' | 'ui_browser_open_failed' | 'cli_iteration' | 'cli_error' | 'loader_dir_unreadable';
|
|
7
|
+
export type ErrorClass = 'usage' | 'not_found' | 'io' | 'port_in_use' | 'unknown';
|
|
8
|
+
export interface LogFields {
|
|
9
|
+
status?: number | string;
|
|
10
|
+
duration_ms?: number;
|
|
11
|
+
error_class?: ErrorClass;
|
|
12
|
+
count?: number;
|
|
13
|
+
current?: number;
|
|
14
|
+
total?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function jsonLogEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
17
|
+
export declare function classifyError(err: unknown): ErrorClass;
|
|
18
|
+
export declare function diag(event: LogEvent, fields: LogFields, legacy?: string): void;
|
|
19
|
+
export declare function logRequest(status: number, startedAt: bigint): void;
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in structured diagnostics. With PROMPTARGS_LOG=json, diagnostics are
|
|
3
|
+
* written to stderr as one JSON object per line with a fixed, bounded field
|
|
4
|
+
* set. Template contents, argument values, paths and URLs are never logged.
|
|
5
|
+
*/
|
|
6
|
+
export function jsonLogEnabled(env = process.env) {
|
|
7
|
+
return env.PROMPTARGS_LOG === 'json';
|
|
8
|
+
}
|
|
9
|
+
export function classifyError(err) {
|
|
10
|
+
const code = err?.code;
|
|
11
|
+
if (code === 'ENOENT')
|
|
12
|
+
return 'not_found';
|
|
13
|
+
if (code === 'EADDRINUSE')
|
|
14
|
+
return 'port_in_use';
|
|
15
|
+
if (typeof code === 'string' && code.startsWith('E'))
|
|
16
|
+
return 'io';
|
|
17
|
+
if (err?.name === 'UsageError')
|
|
18
|
+
return 'usage';
|
|
19
|
+
return 'unknown';
|
|
20
|
+
}
|
|
21
|
+
// Writes a JSON line when enabled; otherwise prints the legacy text fallback
|
|
22
|
+
// (when given) so default output stays byte-for-byte unchanged.
|
|
23
|
+
export function diag(event, fields, legacy) {
|
|
24
|
+
if (!jsonLogEnabled()) {
|
|
25
|
+
if (legacy !== undefined)
|
|
26
|
+
console.error(legacy);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const line = { event };
|
|
30
|
+
for (const key of ['status', 'duration_ms', 'error_class', 'count', 'current', 'total']) {
|
|
31
|
+
if (fields[key] !== undefined)
|
|
32
|
+
line[key] = fields[key];
|
|
33
|
+
}
|
|
34
|
+
console.error(JSON.stringify(line));
|
|
35
|
+
}
|
|
36
|
+
// Request logging has no legacy output, so it is silent unless enabled.
|
|
37
|
+
export function logRequest(status, startedAt) {
|
|
38
|
+
const duration_ms = Number((process.hrtime.bigint() - startedAt) / 1000n) / 1000;
|
|
39
|
+
diag('ui_request', { status, duration_ms });
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,OAAO,GAAG,CAAC,cAAc,KAAK,MAAM,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,IAAI,GAAI,GAAyC,EAAE,IAAI,CAAC;IAC9D,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,WAAW,CAAC;IAC1C,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,aAAa,CAAC;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClE,IAAK,GAAyB,EAAE,IAAI,KAAK,YAAY;QAAE,OAAO,OAAO,CAAC;IACtE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6EAA6E;AAC7E,gEAAgE;AAChE,MAAM,UAAU,IAAI,CAAC,KAAe,EAAE,MAAiB,EAAE,MAAe;IACtE,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QACtB,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAU,EAAE,CAAC;QACjG,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,SAAiB;IAC1D,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC;IACjF,IAAI,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/parser.js
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
* Uses Mustache for expansion, with a promptargs extension for defaults.
|
|
4
4
|
*/
|
|
5
5
|
import Mustache from 'mustache';
|
|
6
|
+
// Kept local (not imported from flags.ts) because ui.html loads parser.js in the browser,
|
|
7
|
+
// where only parser.js/iterate.js/mustache.mjs are served (see STATIC_JS_ROUTES in ui.ts).
|
|
8
|
+
function usageError(message) {
|
|
9
|
+
const e = new Error(message);
|
|
10
|
+
e.name = 'UsageError';
|
|
11
|
+
return e;
|
|
12
|
+
}
|
|
6
13
|
// Exported so any code that needs to recognize the same {{var}} / {{var=default}}
|
|
7
14
|
// grammar (e.g. CLI syntax highlighting) reuses this single definition instead
|
|
8
15
|
// of re-declaring the pattern and risking drift.
|
|
@@ -13,15 +20,34 @@ import Mustache from 'mustache';
|
|
|
13
20
|
// as an empty string — the blank silently vanished instead of being asked for,
|
|
14
21
|
// filled from --flags, highlighted by `show`, or preserved as {{name}}.
|
|
15
22
|
// Trailing padding after a default is not part of the default.
|
|
16
|
-
export const VAR_PATTERN = /\{\{\s*(\w+)(?:=([^}]*?))?\s*\}\}/g;
|
|
23
|
+
export const VAR_PATTERN = /\{\{\s*([\w-]+)(?:=([^}]*?))?\s*\}\}/g;
|
|
24
|
+
// Dotted names ({{repo.owner}}) are Mustache context paths, which promptargs
|
|
25
|
+
// never fills; they would silently render empty, so they are rejected.
|
|
26
|
+
// The lookbehind skips triple-mustache tags; sigil tags ({{#x}}, {{!c}}) do
|
|
27
|
+
// not start with a name character and never match.
|
|
28
|
+
const DOTTED_PATTERN = /(?<!\{)\{\{\s*([\w-]+\.[\w.-]*)\s*(?:=[^}]*)?\}\}/;
|
|
17
29
|
export function parseVars(template) {
|
|
18
|
-
const seen = new Set();
|
|
19
30
|
const vars = [];
|
|
31
|
+
// The first offending tag aborts; the message is built from the name only
|
|
32
|
+
// (restricted to [\w.-]) so raw template bytes never reach the terminal.
|
|
33
|
+
const dotted = DOTTED_PATTERN.exec(template);
|
|
34
|
+
if (dotted) {
|
|
35
|
+
const name = dotted[1];
|
|
36
|
+
throw usageError(`Unsupported variable name "${name}": dotted names are not supported; use {{${name.replace(/\./g, '_')}}}`);
|
|
37
|
+
}
|
|
20
38
|
for (const match of template.matchAll(VAR_PATTERN)) {
|
|
21
39
|
const name = match[1];
|
|
22
|
-
|
|
40
|
+
const existing = vars.find((v) => v.name === name);
|
|
41
|
+
if (existing) {
|
|
42
|
+
const dflt = match[2];
|
|
43
|
+
if (dflt === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
// First default wins; later occurrences may restate it (e.g. a code
|
|
46
|
+
// fence using a different casing) without being an error.
|
|
47
|
+
if (existing.defaultValue === undefined)
|
|
48
|
+
existing.defaultValue = dflt;
|
|
23
49
|
continue;
|
|
24
|
-
|
|
50
|
+
}
|
|
25
51
|
vars.push({
|
|
26
52
|
name,
|
|
27
53
|
defaultValue: match[2],
|
|
@@ -32,13 +58,13 @@ export function parseVars(template) {
|
|
|
32
58
|
}
|
|
33
59
|
export function expand(template, values) {
|
|
34
60
|
const vars = parseVars(template);
|
|
35
|
-
const view =
|
|
61
|
+
const view = Object.assign(Object.create(null), values);
|
|
36
62
|
for (const v of vars) {
|
|
37
|
-
if (!(v.name
|
|
63
|
+
if (!Object.hasOwn(view, v.name) && v.defaultValue !== undefined) {
|
|
38
64
|
view[v.name] = v.defaultValue;
|
|
39
65
|
}
|
|
40
66
|
// Preserve unfilled vars as {{name}} in output
|
|
41
|
-
if (!(v.name
|
|
67
|
+
if (!Object.hasOwn(view, v.name)) {
|
|
42
68
|
view[v.name] = `{{${v.name}}}`;
|
|
43
69
|
}
|
|
44
70
|
}
|
package/dist/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,QAAQ,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/resolver.d.ts
CHANGED
|
@@ -6,5 +6,7 @@ import type { TemplateVar } from './parser.js';
|
|
|
6
6
|
export interface ResolvedValues {
|
|
7
7
|
values: Record<string, string>;
|
|
8
8
|
iterations: Record<string, string>[];
|
|
9
|
+
/** Names whose value was filled from the template default. */
|
|
10
|
+
defaulted: Set<string>;
|
|
9
11
|
}
|
|
10
12
|
export declare function resolve(vars: TemplateVar[], flags: Record<string, string>, interactive: boolean, cross?: boolean): Promise<ResolvedValues>;
|