@hivecommons/promptargs 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/dist/cli.js ADDED
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * promptargs CLI — Template arguments for AI prompts.
4
+ *
5
+ * Usage:
6
+ * promptargs <template> # interactive mode
7
+ * promptargs <template> --var=value # fill from flags
8
+ * promptargs list # show available templates
9
+ * promptargs init # create .prompts/ with examples
10
+ * promptargs show <template> # show template with vars highlighted
11
+ */
12
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { parseVars, expand } from './parser.js';
15
+ import { findTemplate, loadTemplates } from './loader.js';
16
+ import { resolve } from './resolver.js';
17
+ import { renderStatus } from './status.js';
18
+ import { autodetect, AUTODETECT_VARS } from './autodetect.js';
19
+ import { startUI } from './ui.js';
20
+ const HELP = `
21
+ promptargs — Template arguments for AI prompts 🏴‍☠️
22
+
23
+ Usage:
24
+ promptargs <template> Run a template (interactive)
25
+ promptargs <template> --var=value Fill variables from flags
26
+ promptargs list Show available templates
27
+ promptargs init Create .prompts/ with examples
28
+ promptargs show <template> Preview a template
29
+ promptargs env Show auto-detected variable values
30
+ promptargs ui Open the builder UI in your browser
31
+ promptargs help Show this help
32
+
33
+ Flags:
34
+ --no-interactive Skip interactive prompts (use defaults/auto only)
35
+ --cross Cross-product mode: all combinations of array values
36
+ --parallel Run array iterations in parallel (print all at once)
37
+ --json Output as JSON instead of plain text
38
+ --status Print status line only (for IDE integration)
39
+
40
+ Array values:
41
+ --file=a.go,b.go Comma-separated → one run per value
42
+ --file="src/*.go" Glob → expands to matching files
43
+ --file=@list.txt File → one value per line
44
+
45
+ Iteration modes:
46
+ Default (zip): --file=a,b --focus=x,y → 2 runs (a+x, b+y)
47
+ Cross-product: --file=a,b --focus=x,y --cross → 4 runs (a+x, a+y, b+x, b+y)
48
+
49
+ Templates live in:
50
+ .prompts/ Project-level (checked into repo)
51
+ ~/.prompts/ User-level (personal templates)
52
+ `;
53
+ const EXAMPLE_REVIEW = `Review {{file}} for {{focus=correctness}} issues.
54
+ Be {{tone=concise}} in your feedback.
55
+ Focus on real bugs, not style nitpicks.
56
+ `;
57
+ const EXAMPLE_EXPLAIN = `Explain what {{file}} does in {{style=simple}} terms.
58
+ Assume the reader is a {{audience=junior developer}}.
59
+ `;
60
+ const EXAMPLE_FIX = `Fix the {{issue}} in {{file}}.
61
+ The expected behavior is: {{expected}}
62
+ The actual behavior is: {{actual}}
63
+ `;
64
+ async function main() {
65
+ const args = process.argv.slice(2);
66
+ if (args.length === 0 || args[0] === 'help' || args[0] === '--help') {
67
+ console.log(HELP);
68
+ printEnv();
69
+ return;
70
+ }
71
+ if (args[0] === 'env') {
72
+ printEnv();
73
+ return;
74
+ }
75
+ if (args[0] === 'ui') {
76
+ const flags = parseFlags(args.slice(1));
77
+ const port = flags['port'] ? Number(flags['port']) : undefined;
78
+ startUI(port);
79
+ return;
80
+ }
81
+ if (args[0] === 'init') {
82
+ return doInit();
83
+ }
84
+ if (args[0] === 'list') {
85
+ return doList();
86
+ }
87
+ if (args[0] === 'show') {
88
+ return doShow(args[1]);
89
+ }
90
+ // Run a template
91
+ const templateName = args[0];
92
+ const flags = parseFlags(args.slice(1));
93
+ const interactive = !flags['no-interactive'];
94
+ const cross = 'cross' in flags;
95
+ const asJson = 'json' in flags;
96
+ const statusOnly = 'status' in flags;
97
+ // Check for inline template (quoted string with {{vars}})
98
+ let content;
99
+ let name;
100
+ if (templateName.includes('{{')) {
101
+ content = templateName;
102
+ name = 'inline';
103
+ }
104
+ else {
105
+ const tpl = findTemplate(templateName);
106
+ if (!tpl) {
107
+ console.error(`Template "${templateName}" not found.`);
108
+ console.error('Run "promptargs list" to see available templates.');
109
+ console.error('Run "promptargs init" to create example templates.');
110
+ process.exit(1);
111
+ return;
112
+ }
113
+ content = tpl.content;
114
+ name = tpl.name;
115
+ }
116
+ const vars = parseVars(content);
117
+ if (vars.length === 0) {
118
+ // No variables — just output the template as-is
119
+ console.log(content);
120
+ return;
121
+ }
122
+ const { iterations } = await resolve(vars, flags, interactive, cross);
123
+ if (statusOnly) {
124
+ const status = renderStatus(name, vars, iterations[0]);
125
+ console.log(status);
126
+ return;
127
+ }
128
+ const results = [];
129
+ for (let i = 0; i < iterations.length; i++) {
130
+ const values = iterations[i];
131
+ const expanded = expand(content, values);
132
+ if (iterations.length > 1) {
133
+ const status = renderStatus(name, vars, values, {
134
+ current: i + 1,
135
+ total: iterations.length,
136
+ });
137
+ console.error(status);
138
+ }
139
+ results.push(expanded);
140
+ }
141
+ if (asJson) {
142
+ console.log(JSON.stringify(results.length === 1 ? results[0] : results, null, 2));
143
+ }
144
+ else {
145
+ console.log(results.join('\n---\n'));
146
+ }
147
+ }
148
+ function parseFlags(args) {
149
+ const flags = {};
150
+ for (const arg of args) {
151
+ if (arg.startsWith('--')) {
152
+ const eq = arg.indexOf('=');
153
+ if (eq > 0) {
154
+ flags[arg.slice(2, eq)] = arg.slice(eq + 1);
155
+ }
156
+ else {
157
+ flags[arg.slice(2)] = 'true';
158
+ }
159
+ }
160
+ }
161
+ return flags;
162
+ }
163
+ function doInit() {
164
+ const dir = join(process.cwd(), '.prompts');
165
+ if (existsSync(dir)) {
166
+ console.log('.prompts/ already exists!');
167
+ return;
168
+ }
169
+ mkdirSync(dir, { recursive: true });
170
+ writeFileSync(join(dir, 'review.md'), EXAMPLE_REVIEW);
171
+ writeFileSync(join(dir, 'explain.md'), EXAMPLE_EXPLAIN);
172
+ writeFileSync(join(dir, 'fix.md'), EXAMPLE_FIX);
173
+ console.log('Created .prompts/ with 3 example templates:');
174
+ console.log(' review.md — Code review with focus area');
175
+ console.log(' explain.md — Explain code simply');
176
+ console.log(' fix.md — Bug fix template');
177
+ console.log('');
178
+ console.log('Try it: promptargs review --file=src/main.ts');
179
+ }
180
+ function doList() {
181
+ const templates = loadTemplates();
182
+ if (templates.length === 0) {
183
+ console.log('No templates found.');
184
+ console.log('Run "promptargs init" to create example templates.');
185
+ return;
186
+ }
187
+ console.log('Available templates:\n');
188
+ for (const t of templates) {
189
+ const vars = parseVars(t.content);
190
+ const varNames = vars.map(v => {
191
+ if (v.defaultValue !== undefined)
192
+ return `${v.name}=${v.defaultValue}`;
193
+ return v.name;
194
+ });
195
+ const source = t.source === 'user' ? ' (user)' : '';
196
+ console.log(` ${t.name}${source}`);
197
+ console.log(` vars: {{${varNames.join('}} {{')}}}`);
198
+ console.log('');
199
+ }
200
+ }
201
+ function doShow(templateName) {
202
+ if (!templateName) {
203
+ console.error('Usage: promptargs show <template>');
204
+ process.exit(1);
205
+ return;
206
+ }
207
+ const tpl = findTemplate(templateName);
208
+ if (!tpl) {
209
+ console.error(`Template "${templateName}" not found.`);
210
+ process.exit(1);
211
+ return;
212
+ }
213
+ const highlighted = tpl.content.replace(/\{\{(\w+)(?:=([^}]*))?\}\}/g, (_m, varName, defaultVal) => {
214
+ if (defaultVal !== undefined) {
215
+ return `\x1b[33m{{${varName}=${defaultVal}}}\x1b[0m`;
216
+ }
217
+ return `\x1b[31m{{${varName}}}\x1b[0m`;
218
+ });
219
+ console.log(`Template: ${tpl.name} (${tpl.path})\n`);
220
+ console.log(highlighted);
221
+ console.log('\n\x1b[31mred\x1b[0m = required \x1b[33myellow\x1b[0m = has default');
222
+ }
223
+ const DIFF_PREVIEW_MAX_CHARS = 60;
224
+ function printEnv() {
225
+ console.log('Auto-detected variables:\n');
226
+ for (const name of AUTODETECT_VARS) {
227
+ const value = autodetect(name);
228
+ if (value !== undefined) {
229
+ let display = value;
230
+ if (name === 'diff' && display.length > DIFF_PREVIEW_MAX_CHARS) {
231
+ display = display.slice(0, DIFF_PREVIEW_MAX_CHARS) + '...';
232
+ }
233
+ console.log(` \x1b[32m{{${name}}}\x1b[0m = ${display}`);
234
+ }
235
+ else {
236
+ console.log(` \x1b[90m{{${name}}}\x1b[0m = (not detected)`);
237
+ }
238
+ }
239
+ console.log('');
240
+ }
241
+ main().catch(err => {
242
+ console.error(err.message);
243
+ process.exit(1);
244
+ });
245
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { parseVars, expand } from './parser.js';
2
+ export type { TemplateVar } from './parser.js';
3
+ export { autodetect, autodetectAll, AUTODETECT_VARS } from './autodetect.js';
4
+ export { loadTemplates, findTemplate } from './loader.js';
5
+ export type { PromptTemplate } from './loader.js';
6
+ export { resolve } from './resolver.js';
7
+ export type { ResolvedValues } from './resolver.js';
8
+ export { renderStatus } from './status.js';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { parseVars, expand } from './parser.js';
2
+ export { autodetect, autodetectAll, AUTODETECT_VARS } from './autodetect.js';
3
+ export { loadTemplates, findTemplate } from './loader.js';
4
+ export { resolve } from './resolver.js';
5
+ export { renderStatus } from './status.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Find and load prompt templates from .prompts/ directories.
3
+ */
4
+ export interface PromptTemplate {
5
+ name: string;
6
+ content: string;
7
+ path: string;
8
+ source: 'project' | 'user';
9
+ }
10
+ export declare function loadTemplates(cwd?: string): PromptTemplate[];
11
+ export declare function findTemplate(name: string, cwd?: string): PromptTemplate | undefined;
package/dist/loader.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Find and load prompt templates from .prompts/ directories.
3
+ */
4
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
5
+ import { join, basename, extname } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ const PROJECT_DIR = '.prompts';
8
+ const USER_DIR = join(homedir(), '.prompts');
9
+ function loadFromDir(dir, source) {
10
+ if (!existsSync(dir))
11
+ return [];
12
+ return readdirSync(dir)
13
+ .filter((f) => f.endsWith('.md'))
14
+ .map((f) => ({
15
+ name: basename(f, extname(f)),
16
+ content: readFileSync(join(dir, f), 'utf8'),
17
+ path: join(dir, f),
18
+ source,
19
+ }));
20
+ }
21
+ export function loadTemplates(cwd) {
22
+ const projectDir = join(cwd || process.cwd(), PROJECT_DIR);
23
+ const projectTemplates = loadFromDir(projectDir, 'project');
24
+ const userTemplates = loadFromDir(USER_DIR, 'user');
25
+ // Project templates override user templates with same name
26
+ const byName = new Map();
27
+ for (const t of userTemplates)
28
+ byName.set(t.name, t);
29
+ for (const t of projectTemplates)
30
+ byName.set(t.name, t);
31
+ return Array.from(byName.values());
32
+ }
33
+ export function findTemplate(name, cwd) {
34
+ return loadTemplates(cwd).find(t => t.name === name);
35
+ }
36
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +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;AAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AAS7C,SAAS,WAAW,CAAC,GAAW,EAAE,MAA0B;IAC1D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhC,OAAO,WAAW,CAAC,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SACxC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC;QACnB,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7B,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAC3C,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAClB,MAAM;KACP,CAAC,CAAC,CAAC;AACR,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC5D,MAAM,aAAa,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEpD,2DAA2D;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,aAAa;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrD,KAAK,MAAM,CAAC,IAAI,gBAAgB;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAExD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,GAAY;IACrD,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parse {{variables}} and {{variables=defaults}} from template strings.
3
+ * Uses Mustache for expansion, with a promptargs extension for defaults.
4
+ */
5
+ export interface TemplateVar {
6
+ name: string;
7
+ defaultValue?: string;
8
+ raw: string;
9
+ }
10
+ export declare function parseVars(template: string): TemplateVar[];
11
+ export declare function expand(template: string, values: Record<string, string>): string;
package/dist/parser.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Parse {{variables}} and {{variables=defaults}} from template strings.
3
+ * Uses Mustache for expansion, with a promptargs extension for defaults.
4
+ */
5
+ import Mustache from 'mustache';
6
+ const VAR_PATTERN = /\{\{(\w+)(?:=([^}]*))?\}\}/g;
7
+ export function parseVars(template) {
8
+ const seen = new Set();
9
+ const vars = [];
10
+ for (const match of template.matchAll(VAR_PATTERN)) {
11
+ const name = match[1];
12
+ if (seen.has(name))
13
+ continue;
14
+ seen.add(name);
15
+ vars.push({
16
+ name,
17
+ defaultValue: match[2],
18
+ raw: match[0],
19
+ });
20
+ }
21
+ return vars;
22
+ }
23
+ export function expand(template, values) {
24
+ const vars = parseVars(template);
25
+ const view = { ...values };
26
+ for (const v of vars) {
27
+ if (!(v.name in view) && v.defaultValue !== undefined) {
28
+ view[v.name] = v.defaultValue;
29
+ }
30
+ // Preserve unfilled vars as {{name}} in output
31
+ if (!(v.name in view)) {
32
+ view[v.name] = `{{${v.name}}}`;
33
+ }
34
+ }
35
+ // Convert {{var=default}} to {{var}} so Mustache can handle it
36
+ const normalized = template.replace(VAR_PATTERN, (_match, name) => `{{${name}}}`);
37
+ // Disable HTML escaping — we want raw text output
38
+ Mustache.escape = (text) => text;
39
+ return Mustache.render(normalized, view);
40
+ }
41
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,QAAQ,MAAM,UAAU,CAAC;AAQhC,MAAM,WAAW,GAAG,6BAA6B,CAAC;AAElD,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,CAAC;YACR,IAAI;YACJ,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;YACtB,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;SACd,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,QAAgB,EAAE,MAA8B;IACrE,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,IAAI,GAA2B,EAAE,GAAG,MAAM,EAAE,CAAC;IAEnD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACtD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC;QAChC,CAAC;QACD,+CAA+C;QAC/C,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC;QACjC,CAAC;IACH,CAAC;IAED,+DAA+D;IAC/D,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAE1F,kDAAkD;IAClD,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC;IAEzC,OAAO,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { parseVars, expand } from './parser.js';
4
+ describe('parseVars', () => {
5
+ it('finds simple variables', () => {
6
+ const vars = parseVars('Hello {{name}}, welcome to {{place}}');
7
+ assert.strictEqual(vars.length, 2);
8
+ assert.strictEqual(vars[0].name, 'name');
9
+ assert.strictEqual(vars[1].name, 'place');
10
+ });
11
+ it('finds variables with defaults', () => {
12
+ const vars = parseVars('Be {{tone=concise}} please');
13
+ assert.strictEqual(vars.length, 1);
14
+ assert.strictEqual(vars[0].name, 'tone');
15
+ assert.strictEqual(vars[0].defaultValue, 'concise');
16
+ });
17
+ it('deduplicates variables', () => {
18
+ const vars = parseVars('{{name}} said hi to {{name}}');
19
+ assert.strictEqual(vars.length, 1);
20
+ });
21
+ it('returns empty for no variables', () => {
22
+ const vars = parseVars('No variables here');
23
+ assert.strictEqual(vars.length, 0);
24
+ });
25
+ });
26
+ describe('expand', () => {
27
+ it('substitutes values', () => {
28
+ const result = expand('Hello {{name}}!', { name: 'World' });
29
+ assert.strictEqual(result, 'Hello World!');
30
+ });
31
+ it('uses defaults when no value provided', () => {
32
+ const result = expand('Be {{tone=concise}}', {});
33
+ assert.strictEqual(result, 'Be concise');
34
+ });
35
+ it('overrides defaults with provided values', () => {
36
+ const result = expand('Be {{tone=concise}}', { tone: 'verbose' });
37
+ assert.strictEqual(result, 'Be verbose');
38
+ });
39
+ it('leaves unfilled vars as-is', () => {
40
+ const result = expand('Hello {{name}}', {});
41
+ assert.strictEqual(result, 'Hello {{name}}');
42
+ });
43
+ it('handles multiple vars', () => {
44
+ const result = expand('Review {{file}} for {{focus=correctness}}. Be {{tone=concise}}.', { file: 'main.go' });
45
+ assert.strictEqual(result, 'Review main.go for correctness. Be concise.');
46
+ });
47
+ it('handles arrays in comma-separated values', () => {
48
+ const result = expand('Check {{file}}', { file: 'a.go' });
49
+ assert.strictEqual(result, 'Check a.go');
50
+ });
51
+ it('does not HTML-escape values', () => {
52
+ const result = expand('{{code}}', { code: '<div class="test">' });
53
+ assert.strictEqual(result, '<div class="test">');
54
+ });
55
+ });
56
+ //# sourceMappingURL=parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../src/parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resolve variable values: flags → auto-detect → defaults → ask.
3
+ * Handles array expansion (comma-separated, globs, @file, stdin).
4
+ */
5
+ import type { TemplateVar } from './parser.js';
6
+ export interface ResolvedValues {
7
+ values: Record<string, string>;
8
+ iterations: Record<string, string>[];
9
+ }
10
+ export declare function resolve(vars: TemplateVar[], flags: Record<string, string>, interactive: boolean, cross?: boolean): Promise<ResolvedValues>;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Resolve variable values: flags → auto-detect → defaults → ask.
3
+ * Handles array expansion (comma-separated, globs, @file, stdin).
4
+ */
5
+ import { createInterface } from 'node:readline';
6
+ import { readFileSync, existsSync } from 'node:fs';
7
+ import { execSync } from 'node:child_process';
8
+ import { autodetect } from './autodetect.js';
9
+ function expandArrayValue(raw) {
10
+ // @file — read lines from file
11
+ if (raw.startsWith('@') && existsSync(raw.slice(1))) {
12
+ return readFileSync(raw.slice(1), 'utf8')
13
+ .split('\n')
14
+ .map((l) => l.trim())
15
+ .filter(Boolean);
16
+ }
17
+ // - (stdin) handled at CLI level before this
18
+ if (raw === '-')
19
+ return [raw];
20
+ // Glob pattern
21
+ if (raw.includes('*') || raw.includes('?')) {
22
+ try {
23
+ const files = execSync(`ls -1 ${raw}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
24
+ .trim()
25
+ .split('\n')
26
+ .filter(Boolean);
27
+ if (files.length > 0)
28
+ return files;
29
+ }
30
+ catch {
31
+ // Not a valid glob, treat as literal
32
+ }
33
+ }
34
+ // Comma-separated
35
+ if (raw.includes(',')) {
36
+ return raw.split(',').map(s => s.trim()).filter(Boolean);
37
+ }
38
+ return [raw];
39
+ }
40
+ function cartesian(arrays) {
41
+ if (arrays.length === 0)
42
+ return [[]];
43
+ const [first, ...rest] = arrays;
44
+ const restCombos = cartesian(rest);
45
+ const result = [];
46
+ for (const val of first) {
47
+ for (const combo of restCombos) {
48
+ result.push([val, ...combo]);
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+ export async function resolve(vars, flags, interactive, cross = false) {
54
+ const values = {};
55
+ const arrayVars = {};
56
+ let hasArrays = false;
57
+ for (const v of vars) {
58
+ // 1. Check flags
59
+ if (v.name in flags) {
60
+ const expanded = expandArrayValue(flags[v.name]);
61
+ if (expanded.length > 1) {
62
+ arrayVars[v.name] = expanded;
63
+ hasArrays = true;
64
+ }
65
+ else {
66
+ values[v.name] = expanded[0];
67
+ }
68
+ continue;
69
+ }
70
+ // 2. Auto-detect
71
+ const auto = autodetect(v.name);
72
+ if (auto !== undefined) {
73
+ values[v.name] = auto;
74
+ continue;
75
+ }
76
+ // 3. Default
77
+ if (v.defaultValue !== undefined) {
78
+ values[v.name] = v.defaultValue;
79
+ continue;
80
+ }
81
+ // 4. Ask interactively
82
+ if (interactive) {
83
+ const answer = await ask(` ${v.name}: `);
84
+ const expanded = expandArrayValue(answer);
85
+ if (expanded.length > 1) {
86
+ arrayVars[v.name] = expanded;
87
+ hasArrays = true;
88
+ }
89
+ else {
90
+ values[v.name] = expanded[0];
91
+ }
92
+ }
93
+ }
94
+ // Build iterations
95
+ if (!hasArrays) {
96
+ return { values, iterations: [values] };
97
+ }
98
+ const arrayNames = Object.keys(arrayVars);
99
+ const iterations = [];
100
+ if (cross) {
101
+ const arrays = arrayNames.map(n => arrayVars[n]);
102
+ const combos = cartesian(arrays);
103
+ for (const combo of combos) {
104
+ const iter = { ...values };
105
+ for (let j = 0; j < arrayNames.length; j++) {
106
+ iter[arrayNames[j]] = combo[j];
107
+ }
108
+ iterations.push(iter);
109
+ }
110
+ }
111
+ else {
112
+ const maxLen = Math.max(...arrayNames.map(n => arrayVars[n].length));
113
+ for (let i = 0; i < maxLen; i++) {
114
+ const iter = { ...values };
115
+ for (const name of arrayNames) {
116
+ const arr = arrayVars[name];
117
+ iter[name] = arr[i % arr.length];
118
+ }
119
+ iterations.push(iter);
120
+ }
121
+ }
122
+ return { values, iterations };
123
+ }
124
+ function ask(prompt) {
125
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
126
+ return new Promise(resolve => {
127
+ rl.question(prompt, (answer) => {
128
+ rl.close();
129
+ resolve(answer.trim());
130
+ });
131
+ });
132
+ }
133
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Status line rendering for Claude Code's status area.
3
+ * Shows current template state: filled vars, unfilled vars, progress.
4
+ */
5
+ import type { TemplateVar } from './parser.js';
6
+ export declare function renderStatus(templateName: string, vars: TemplateVar[], values: Record<string, string>, iteration?: {
7
+ current: number;
8
+ total: number;
9
+ }): string;
package/dist/status.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Status line rendering for Claude Code's status area.
3
+ * Shows current template state: filled vars, unfilled vars, progress.
4
+ */
5
+ export function renderStatus(templateName, vars, values, iteration) {
6
+ const parts = [];
7
+ for (const v of vars) {
8
+ const val = values[v.name];
9
+ if (val !== undefined) {
10
+ const source = v.defaultValue === val ? '(default)' : '';
11
+ parts.push(`${v.name}=${val}${source}`);
12
+ }
13
+ else {
14
+ parts.push(`${v.name}=___`);
15
+ }
16
+ }
17
+ const allFilled = vars.every(v => v.name in values);
18
+ const icon = allFilled ? '✅' : '📋';
19
+ const progress = iteration ? ` [${iteration.current}/${iteration.total}]` : '';
20
+ return `${icon} ${templateName}${progress}: ${parts.join(' ')}`;
21
+ }
22
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,UAAU,YAAY,CAC1B,YAAoB,EACpB,IAAmB,EACnB,MAA8B,EAC9B,SAA8C;IAE9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/E,OAAO,GAAG,IAAI,IAAI,YAAY,GAAG,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACnE,CAAC"}
package/dist/ui.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function startUI(port?: number): void;