agentic-workflow-manager 2.1.0 → 3.0.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/src/index.js CHANGED
@@ -9,6 +9,8 @@ const commander_1 = require("commander");
9
9
  const config_1 = require("./utils/config");
10
10
  const grouping_1 = require("./utils/grouping");
11
11
  const registry_view_1 = require("./utils/registry-view");
12
+ const tty_1 = require("./ui/tty");
13
+ const picker_1 = require("./ui/picker");
12
14
  const providers_1 = require("./providers");
13
15
  const executor_1 = require("./core/executor");
14
16
  const regenerate_1 = require("./core/context/regenerate");
@@ -73,6 +75,7 @@ program.command('add [name]')
73
75
  .option('-s, --scope <scope>', 'Scope: local or global')
74
76
  .option('-m, --method <method>', 'Install method: symlink or copy')
75
77
  .option('-y, --yes', 'Skip confirmation prompts')
78
+ .option('--all', 'install all artifacts from all packages without prompting')
76
79
  .action(async (name, options) => {
77
80
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Agentic Workflow Manager ')));
78
81
  // 1. Sync the registry
@@ -142,6 +145,10 @@ program.command('add [name]')
142
145
  (0, prompts_1.outro)(`✅ Installed bundle ${picocolors_1.default.cyan(matchedBundle.name)}:\n ${lines}${recordNote}`);
143
146
  return;
144
147
  }
148
+ // name given but no matching bundle found — report clearly regardless of TTY
149
+ console.error(picocolors_1.default.red(`Bundle "${name}" not found in registry.`));
150
+ console.error(picocolors_1.default.dim('Run `awm list` to see available packages.'));
151
+ process.exit(1);
145
152
  }
146
153
  // 2. Discover artifacts
147
154
  const skills = (0, discovery_1.discoverSkills)();
@@ -152,6 +159,103 @@ program.command('add [name]')
152
159
  process.exit(0);
153
160
  }
154
161
  const prefs = (0, config_1.getPreferences)();
162
+ // --all: install everything from every package headlessly
163
+ if (options.all) {
164
+ let targetAgents;
165
+ if (options.agent) {
166
+ const validAgents = Object.keys(providers_1.PROVIDERS);
167
+ const parsed = options.agent.split(',').map((a) => a.trim());
168
+ for (const a of parsed) {
169
+ if (!validAgents.includes(a)) {
170
+ console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${validAgents.join(', ')}.`));
171
+ process.exit(1);
172
+ }
173
+ }
174
+ targetAgents = parsed;
175
+ }
176
+ else {
177
+ targetAgents = [prefs.defaultAgent];
178
+ }
179
+ let scopeVal;
180
+ if (options.scope) {
181
+ if (!['local', 'global'].includes(options.scope)) {
182
+ console.error(picocolors_1.default.red(`Invalid scope "${options.scope}". Use: local or global.`));
183
+ process.exit(1);
184
+ }
185
+ scopeVal = options.scope;
186
+ }
187
+ else {
188
+ scopeVal = prefs.defaultScope;
189
+ }
190
+ let methodVal;
191
+ if (options.method) {
192
+ if (!['symlink', 'copy'].includes(options.method)) {
193
+ console.error(picocolors_1.default.red(`Invalid method "${options.method}". Use: symlink or copy.`));
194
+ process.exit(1);
195
+ }
196
+ methodVal = options.method;
197
+ }
198
+ else {
199
+ methodVal = scopeVal === 'local' ? 'copy' : 'symlink';
200
+ }
201
+ const includeWorkflows = targetAgents.some((a) => providers_1.PROVIDERS[a].workflow !== null);
202
+ const includeAgents = targetAgents.some((a) => providers_1.PROVIDERS[a].agent !== null);
203
+ const allView = (0, registry_view_1.buildPackageView)(skills, includeWorkflows ? workflows : [], includeAgents ? agents : [], (0, bundles_1.discoverAllBundles)());
204
+ const allDedup = new Map();
205
+ for (const pkg of allView) {
206
+ for (const a of pkg.artifacts) {
207
+ allDedup.set((0, registry_view_1.artifactValue)(a), a);
208
+ }
209
+ }
210
+ const artifactsToInstall = Array.from(allDedup.values()).map((a) => ({
211
+ name: a.installName,
212
+ sourcePath: a.sourcePath,
213
+ type: a.type,
214
+ }));
215
+ if (artifactsToInstall.length === 0) {
216
+ (0, prompts_1.outro)(picocolors_1.default.yellow('No artifacts available.'));
217
+ return;
218
+ }
219
+ const installSpinner = (0, prompts_1.spinner)();
220
+ installSpinner.start('Installing artifacts...');
221
+ try {
222
+ const installed = [];
223
+ const skipped = [];
224
+ for (const currentAgent of targetAgents) {
225
+ for (const artifact of artifactsToInstall) {
226
+ if (providers_1.PROVIDERS[currentAgent][artifact.type] === null) {
227
+ skipped.push(`${artifact.name} (${currentAgent})`);
228
+ continue;
229
+ }
230
+ const targetDir = (0, providers_1.getTargetPath)(artifact.type, currentAgent, scopeVal);
231
+ const finalDest = path_1.default.join(targetDir, artifact.name);
232
+ (0, executor_1.installArtifact)(artifact.sourcePath, finalDest, methodVal);
233
+ installed.push(`${artifact.name} → ${currentAgent} (${scopeVal})`);
234
+ }
235
+ }
236
+ (0, config_1.savePreferences)({ defaultAgent: targetAgents[0], defaultScope: scopeVal, installMethod: methodVal });
237
+ installSpinner.stop('Installation complete!');
238
+ if (skipped.length > 0) {
239
+ for (const sk of skipped) {
240
+ console.log(picocolors_1.default.yellow(` ⚠️ Skipped: ${sk} (not supported by target agent)`));
241
+ }
242
+ }
243
+ const names = installed.map((n) => picocolors_1.default.green(n)).join('\n ');
244
+ (0, prompts_1.outro)(`✅ Installed:\n ${names}`);
245
+ }
246
+ catch (e) {
247
+ installSpinner.stop('Installation failed.');
248
+ console.error(picocolors_1.default.red(e.message));
249
+ process.exit(1);
250
+ }
251
+ return;
252
+ }
253
+ // Interactive selection requires a TTY. Bundle-by-name (handled above) works headless.
254
+ if (!(0, tty_1.isInteractive)()) {
255
+ console.error(picocolors_1.default.red('`awm add` needs an interactive terminal for selection.'));
256
+ console.error(picocolors_1.default.dim('Install a bundle non-interactively: `awm add <bundle>` (optionally with --agent/--scope).'));
257
+ process.exit(1);
258
+ }
155
259
  // 3. Agent & Scope Prompts (Moved up)
156
260
  let targetAgents;
157
261
  if (options.agent) {
@@ -207,27 +311,35 @@ program.command('add [name]')
207
311
  process.exit(0);
208
312
  }
209
313
  // 5. Level 1 — pick package(s)
210
- const pkgChoice = await (0, prompts_1.multiselect)({
211
- message: 'Select package(s)',
212
- options: (0, registry_view_1.buildLevel1Options)(view),
213
- required: true
314
+ const pickedPackages = await (0, picker_1.multiselectPicker)({
315
+ title: 'Select package(s)',
316
+ items: (0, registry_view_1.packagePickerItems)(view),
214
317
  });
215
- handleCancel(pkgChoice);
216
- const selectedPackages = pkgChoice
217
- .map(name => view.find(p => p.name === name))
318
+ if (pickedPackages === null) {
319
+ (0, prompts_1.outro)('Operation cancelled.');
320
+ process.exit(0);
321
+ }
322
+ if (pickedPackages.length === 0) {
323
+ (0, prompts_1.outro)(picocolors_1.default.yellow('No packages selected.'));
324
+ return;
325
+ }
326
+ const selectedPackages = pickedPackages
327
+ .map((name) => view.find((p) => p.name === name))
218
328
  .filter(Boolean);
219
329
  // 5b. Level 2 — pick skills within each package, in sequence
220
330
  const dedup = new Map();
221
331
  for (let i = 0; i < selectedPackages.length; i++) {
222
332
  const pkg = selectedPackages[i];
223
- const skillChoice = await (0, prompts_1.multiselect)({
224
- message: `[${i + 1}/${selectedPackages.length}] ${pkg.name} — select artifacts`,
225
- options: (0, registry_view_1.buildLevel2Options)(pkg),
226
- initialValues: [registry_view_1.ALL_SENTINEL],
227
- required: true
333
+ const picked = await (0, picker_1.multiselectPicker)({
334
+ title: `[${i + 1}/${selectedPackages.length}] ${pkg.name} — select artifacts`,
335
+ items: (0, registry_view_1.artifactPickerItems)(pkg),
336
+ initialSelected: [registry_view_1.ALL_SENTINEL, ...pkg.artifacts.map((a) => (0, registry_view_1.artifactValue)(a))],
228
337
  });
229
- handleCancel(skillChoice);
230
- for (const a of (0, registry_view_1.resolveLevel2Selection)(pkg, skillChoice)) {
338
+ if (picked === null) {
339
+ (0, prompts_1.outro)('Operation cancelled.');
340
+ process.exit(0);
341
+ }
342
+ for (const a of (0, registry_view_1.resolveLevel2Selection)(pkg, picked)) {
231
343
  dedup.set((0, registry_view_1.artifactValue)(a), a);
232
344
  }
233
345
  }
@@ -469,6 +581,7 @@ program.command('list [package]')
469
581
  if (packageName && options.all) {
470
582
  console.log(picocolors_1.default.dim('(Ignoring --all when a package name is provided.)'));
471
583
  }
584
+ const listWidth = process.stdout.isTTY ? process.stdout.columns : undefined;
472
585
  // Detail for a single package.
473
586
  if (packageName) {
474
587
  const { match, suggestion } = (0, registry_view_1.findPackage)(fullView, packageName);
@@ -480,7 +593,7 @@ program.command('list [package]')
480
593
  process.exit(1);
481
594
  }
482
595
  console.log();
483
- for (const line of (0, registry_view_1.packageDetailLines)(match))
596
+ for (const line of (0, registry_view_1.packageDetailLines)(match, listWidth))
484
597
  console.log(line);
485
598
  console.log();
486
599
  (0, prompts_1.outro)(`Run ${picocolors_1.default.green(`awm add`)} to install artifacts from ${picocolors_1.default.cyan(match.name)}.`);
@@ -490,7 +603,7 @@ program.command('list [package]')
490
603
  if (options.all) {
491
604
  for (const pkg of view) {
492
605
  console.log();
493
- for (const line of (0, registry_view_1.packageDetailLines)(pkg))
606
+ for (const line of (0, registry_view_1.packageDetailLines)(pkg, listWidth))
494
607
  console.log(line);
495
608
  }
496
609
  console.log();
@@ -499,7 +612,7 @@ program.command('list [package]')
499
612
  }
500
613
  // Default: compact summary.
501
614
  console.log();
502
- for (const line of (0, registry_view_1.packageSummaryLines)(view))
615
+ for (const line of (0, registry_view_1.packageSummaryLines)(view, listWidth))
503
616
  console.log(line);
504
617
  console.log();
505
618
  console.log(picocolors_1.default.dim(` awm list <pkg> · awm list --all`));
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GIT_LOG_FORMAT = exports.US = exports.RS = exports.PKG_NAME = void 0;
4
+ exports.parseCommits = parseCommits;
5
+ exports.determineBump = determineBump;
6
+ exports.nextVersion = nextVersion;
7
+ exports.selectFloor = selectFloor;
8
+ exports.renderChangelog = renderChangelog;
9
+ const versioning_1 = require("../core/versioning");
10
+ exports.PKG_NAME = 'agentic-workflow-manager';
11
+ exports.RS = '\x1e';
12
+ exports.US = '\x1f';
13
+ exports.GIT_LOG_FORMAT = `%s${exports.US}%b${exports.RS}`;
14
+ const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
15
+ const HEADER_RE = /^(\w+)(?:\(([^)]+)\))?(!)?:\s+(.+)$/;
16
+ function parseOne(record) {
17
+ const [header, ...rest] = record.split(exports.US);
18
+ const body = rest.join(exports.US);
19
+ const m = HEADER_RE.exec(header.trim());
20
+ if (!m)
21
+ return null;
22
+ return {
23
+ type: m[1],
24
+ scope: m[2] ?? null,
25
+ breaking: Boolean(m[3]) || /^BREAKING CHANGE:/m.test(body),
26
+ subject: m[4].trim(),
27
+ };
28
+ }
29
+ function parseCommits(raw) {
30
+ return raw
31
+ .split(exports.RS)
32
+ .map((r) => r.trim())
33
+ .filter(Boolean)
34
+ .map(parseOne)
35
+ .filter((c) => c !== null);
36
+ }
37
+ function determineBump(commits) {
38
+ if (commits.some((c) => c.breaking))
39
+ return 'major';
40
+ if (commits.some((c) => c.type === 'feat'))
41
+ return 'minor';
42
+ if (commits.some((c) => c.type === 'fix' || c.type === 'perf'))
43
+ return 'patch';
44
+ return null;
45
+ }
46
+ function nextVersion(base, bump) {
47
+ const m = SEMVER_RE.exec((base ?? '').trim());
48
+ if (!m)
49
+ throw new Error(`Invalid base version: "${base}"`);
50
+ const [maj, min, pat] = [Number(m[1]), Number(m[2]), Number(m[3])];
51
+ if (bump === 'major')
52
+ return `${maj + 1}.0.0`;
53
+ if (bump === 'minor')
54
+ return `${maj}.${min + 1}.0`;
55
+ return `${maj}.${min}.${pat + 1}`;
56
+ }
57
+ function selectFloor(current, lastTagVersion) {
58
+ if (!SEMVER_RE.test((current ?? '').trim())) {
59
+ throw new Error(`Invalid current version: "${current}"`);
60
+ }
61
+ const cur = current.trim();
62
+ if (!lastTagVersion)
63
+ return cur;
64
+ return (0, versioning_1.compareSemver)(cur, lastTagVersion) >= 0 ? cur : lastTagVersion;
65
+ }
66
+ function line(c) {
67
+ return c.scope ? `- **${c.scope}:** ${c.subject}` : `- ${c.subject}`;
68
+ }
69
+ function renderChangelog(version, dateISO, commits) {
70
+ const sections = [`## v${version} - ${dateISO}`, ''];
71
+ const groups = [
72
+ ['Breaking Changes', (c) => c.breaking],
73
+ ['Features', (c) => !c.breaking && c.type === 'feat'],
74
+ ['Fixes', (c) => !c.breaking && (c.type === 'fix' || c.type === 'perf')],
75
+ ];
76
+ for (const [title, pred] of groups) {
77
+ const items = commits.filter(pred);
78
+ if (items.length === 0)
79
+ continue;
80
+ sections.push(`### ${title}`, ...items.map(line), '');
81
+ }
82
+ return sections.join('\n').trimEnd() + '\n';
83
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseArgs = parseArgs;
7
+ exports.main = main;
8
+ // cli/src/release/index.ts
9
+ const child_process_1 = require("child_process");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const orchestrator_1 = require("./orchestrator");
13
+ function parseArgs(argv) {
14
+ const opts = { dryRun: false, force: null, push: true, branch: 'main', cliDir: '' };
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const a = argv[i];
17
+ if (a === '--dry-run')
18
+ opts.dryRun = true;
19
+ else if (a === '--no-push')
20
+ opts.push = false;
21
+ else if (a === '--branch') {
22
+ const val = argv[++i];
23
+ if (val === undefined || val.startsWith('--')) {
24
+ throw new Error('--branch requiere un valor (ej: --branch main)');
25
+ }
26
+ opts.branch = val;
27
+ }
28
+ else if (a === '--force') {
29
+ const lvl = argv[++i];
30
+ if (lvl !== 'major' && lvl !== 'minor' && lvl !== 'patch') {
31
+ throw new Error(`--force requiere major|minor|patch, recibió "${lvl}"`);
32
+ }
33
+ opts.force = lvl;
34
+ }
35
+ else {
36
+ throw new Error(`Flag desconocida: ${a}`);
37
+ }
38
+ }
39
+ return opts;
40
+ }
41
+ function realIO(repoRoot, cliDir) {
42
+ const pkgPath = path_1.default.join(cliDir, 'package.json');
43
+ const changelogPath = path_1.default.join(repoRoot, 'CHANGELOG.md');
44
+ const npmrcPath = path_1.default.join(cliDir, '.npmrc');
45
+ return {
46
+ run(cmd, args, o) {
47
+ return (0, child_process_1.execFileSync)(cmd, args, { cwd: o?.cwd ?? repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
48
+ },
49
+ readPackageVersion: () => JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8')).version,
50
+ writePackageVersion: (v) => {
51
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
52
+ pkg.version = v;
53
+ fs_1.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
54
+ },
55
+ readChangelog: () => (fs_1.default.existsSync(changelogPath) ? fs_1.default.readFileSync(changelogPath, 'utf8') : ''),
56
+ writeChangelog: (c) => fs_1.default.writeFileSync(changelogPath, c),
57
+ writeNpmrc: (token) => fs_1.default.writeFileSync(npmrcPath, `//registry.npmjs.org/:_authToken=${token}\n`),
58
+ removeNpmrc: () => { if (fs_1.default.existsSync(npmrcPath))
59
+ fs_1.default.rmSync(npmrcPath); },
60
+ today: () => new Date().toISOString().slice(0, 10),
61
+ log: (m) => console.log(m),
62
+ env: process.env,
63
+ };
64
+ }
65
+ function main(argv) {
66
+ let opts;
67
+ try {
68
+ opts = parseArgs(argv);
69
+ }
70
+ catch (e) {
71
+ console.error(e.message);
72
+ return 2;
73
+ }
74
+ // __dirname en dist/src/release/ → 4 niveles arriba = repoRoot
75
+ // dist/src/release → dist/src → dist → cli → repoRoot
76
+ const repoRoot = path_1.default.resolve(__dirname, '..', '..', '..', '..');
77
+ const cliDir = path_1.default.resolve(repoRoot, 'cli');
78
+ opts.cliDir = cliDir;
79
+ try {
80
+ const res = (0, orchestrator_1.release)(opts, realIO(repoRoot, cliDir));
81
+ if (res.released)
82
+ console.log(`✓ Publicado v${res.version}`);
83
+ else
84
+ console.log(`· Sin release: ${res.reason}${res.version ? ` (v${res.version})` : ''}`);
85
+ return 0;
86
+ }
87
+ catch (e) {
88
+ console.error(`✗ ${e.message}`);
89
+ return 1;
90
+ }
91
+ }
92
+ if (require.main === module) {
93
+ process.exit(main(process.argv.slice(2)));
94
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.release = release;
4
+ // cli/src/release/orchestrator.ts
5
+ const core_1 = require("./core");
6
+ const TAG_RE = /^v(\d+)\.(\d+)\.(\d+)$/;
7
+ function highestTag(io) {
8
+ const raw = io.run('git', ['tag', '--list', 'v*']).trim();
9
+ const tags = raw.split('\n').map((t) => t.trim()).filter((t) => TAG_RE.test(t));
10
+ if (tags.length === 0)
11
+ return null;
12
+ tags.sort((a, b) => {
13
+ const [, a1, a2, a3] = TAG_RE.exec(a).map(Number);
14
+ const [, b1, b2, b3] = TAG_RE.exec(b).map(Number);
15
+ return a1 - b1 || a2 - b2 || a3 - b3;
16
+ });
17
+ return tags[tags.length - 1];
18
+ }
19
+ function release(opts, io) {
20
+ // --- preflight gates (contrato, antes de cualquier early-exit) ---
21
+ if (!opts.force) {
22
+ const branch = io.run('git', ['rev-parse', '--abbrev-ref', 'HEAD']).trim();
23
+ if (branch !== opts.branch) {
24
+ throw new Error(`Rama actual "${branch}" != esperada "${opts.branch}" (usá --force para relajar)`);
25
+ }
26
+ }
27
+ if (!opts.dryRun) {
28
+ const dirty = io.run('git', ['status', '--porcelain']).trim();
29
+ if (dirty)
30
+ throw new Error('Working tree con cambios sin commitear — abortando');
31
+ if (!io.env.NPM_TOKEN && !io.env.NODE_AUTH_TOKEN && !io.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
32
+ throw new Error('Falta credencial de publicación: configurá NPM_TOKEN, NODE_AUTH_TOKEN, o usa OIDC (id-token: write en GitHub Actions)');
33
+ }
34
+ }
35
+ // baseline
36
+ const current = io.readPackageVersion();
37
+ const lastTag = highestTag(io); // "vX.Y.Z" | null
38
+ const lastTagVersion = lastTag ? lastTag.slice(1) : null;
39
+ const floor = (0, core_1.selectFloor)(current, lastTagVersion);
40
+ // rango de commits (solo cli/)
41
+ const range = lastTag ? [`${lastTag}..HEAD`] : [];
42
+ const logArgs = ['log', ...range, '--no-merges', `--format=${core_1.GIT_LOG_FORMAT}`, '--', 'cli/'];
43
+ const commits = (0, core_1.parseCommits)(io.run('git', logArgs));
44
+ // bump
45
+ const bump = opts.force ?? (0, core_1.determineBump)(commits);
46
+ if (!bump)
47
+ return { released: false, reason: 'nada que publicar (no releasable commits)' };
48
+ const version = (0, core_1.nextVersion)(floor, bump);
49
+ // GATE idempotencia
50
+ if (io.run('git', ['tag', '--list', `v${version}`]).trim()) {
51
+ throw new Error(`El tag v${version} ya existe — abortando`);
52
+ }
53
+ let published = '';
54
+ try {
55
+ published = io.run('npm', ['view', `${core_1.PKG_NAME}@${version}`, 'version']).trim();
56
+ }
57
+ catch {
58
+ published = '';
59
+ }
60
+ if (published)
61
+ throw new Error(`${core_1.PKG_NAME}@${version} ya está publicado en npm — abortando`);
62
+ if (opts.dryRun) {
63
+ io.log(`[dry-run] publicaría v${version} (bump=${bump}, base=${floor})`);
64
+ return { released: false, version, reason: 'dry-run' };
65
+ }
66
+ // aplicar
67
+ io.writePackageVersion(version);
68
+ const section = (0, core_1.renderChangelog)(version, io.today(), commits);
69
+ io.writeChangelog(section + '\n' + io.readChangelog());
70
+ io.run('git', ['add', 'cli/package.json', 'CHANGELOG.md']);
71
+ io.run('git', ['commit', '-m', `chore(release): v${version} [skip ci]`]);
72
+ io.run('git', ['tag', '-a', `v${version}`, '-m', `v${version}`]);
73
+ // OIDC mode: GitHub inyecta ACTIONS_ID_TOKEN_REQUEST_URL con id-token:write
74
+ // o NODE_AUTH_TOKEN sin NPM_TOKEN (setup-node legacy con OIDC)
75
+ // Legacy mode (NPM_TOKEN): write .npmrc temporal, cleanup en finally
76
+ const useOidc = !io.env.NPM_TOKEN &&
77
+ (!!io.env.ACTIONS_ID_TOKEN_REQUEST_URL || !!io.env.NODE_AUTH_TOKEN);
78
+ let publishError;
79
+ if (useOidc) {
80
+ try {
81
+ io.run('npm', ['publish', '--provenance'], { cwd: opts.cliDir });
82
+ }
83
+ catch (e) {
84
+ publishError = e;
85
+ }
86
+ }
87
+ else {
88
+ const token = io.env.NPM_TOKEN;
89
+ try {
90
+ io.writeNpmrc(token);
91
+ io.run('npm', ['publish'], { cwd: opts.cliDir });
92
+ }
93
+ catch (e) {
94
+ publishError = e;
95
+ }
96
+ finally {
97
+ io.removeNpmrc();
98
+ }
99
+ }
100
+ if (publishError) {
101
+ try {
102
+ io.run('git', ['tag', '-d', `v${version}`]);
103
+ }
104
+ catch { /* best effort */ }
105
+ try {
106
+ io.run('git', ['reset', '--hard', 'HEAD~1']);
107
+ }
108
+ catch { /* best effort */ }
109
+ throw publishError;
110
+ }
111
+ if (opts.push) {
112
+ io.run('git', ['push', 'origin', opts.branch]);
113
+ io.run('git', ['push', 'origin', `v${version}`]);
114
+ }
115
+ return { released: true, version };
116
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TWO_PANE_MIN_COLUMNS = void 0;
7
+ exports.visibleItems = visibleItems;
8
+ exports.renderPicker = renderPicker;
9
+ const picocolors_1 = __importDefault(require("picocolors"));
10
+ const text_1 = require("./text");
11
+ exports.TWO_PANE_MIN_COLUMNS = 72;
12
+ const FOOTER = '↑↓ move · space select · tab all · ⏎ confirm · esc clear';
13
+ function visibleItems(state) {
14
+ if (!state.filter)
15
+ return state.items;
16
+ const q = state.filter.toLowerCase();
17
+ return state.items.filter((i) => i.label.toLowerCase().includes(q));
18
+ }
19
+ /** Compute a scroll window [start, end) around the cursor that fits `height` rows. */
20
+ function windowFor(cursor, total, height) {
21
+ if (total <= height)
22
+ return [0, total];
23
+ let start = cursor - Math.floor(height / 2);
24
+ start = Math.max(0, Math.min(start, total - height));
25
+ return [start, start + height];
26
+ }
27
+ function checkbox(selected) {
28
+ return selected ? picocolors_1.default.green('◼') : '◻';
29
+ }
30
+ function renderPicker(state, vp) {
31
+ const vis = visibleItems(state);
32
+ const cursor = Math.max(0, Math.min(state.cursor, vis.length - 1));
33
+ const selCount = state.selected.size;
34
+ const filterPart = state.filter ? ` filter: ${state.filter}` : '';
35
+ const header = picocolors_1.default.bold(state.title) + filterPart + (selCount ? picocolors_1.default.dim(` ${selCount} sel`) : '');
36
+ const out = [header];
37
+ if (vis.length === 0) {
38
+ out.push(picocolors_1.default.dim(' (no matches)'));
39
+ out.push(FOOTER);
40
+ return out;
41
+ }
42
+ const bodyHeight = Math.max(3, vp.rows - 4); // header + footer + breathing room
43
+ const [start, end] = windowFor(cursor, vis.length, bodyHeight);
44
+ const twoPane = vp.columns >= exports.TWO_PANE_MIN_COLUMNS;
45
+ const listWidth = twoPane ? Math.floor(vp.columns * 0.5) : vp.columns - 2;
46
+ const rows = [];
47
+ for (let i = start; i < end; i++) {
48
+ const item = vis[i];
49
+ const isCursor = i === cursor;
50
+ const marker = isCursor ? picocolors_1.default.cyan('❯') : ' ';
51
+ const box = checkbox(state.selected.has(item.value));
52
+ const labelWidth = listWidth - 5; // wide cursor marker (2 cells) + space + checkbox + space
53
+ const label = (0, text_1.truncate)(item.label, labelWidth);
54
+ rows.push(`${marker} ${box} ${label}`);
55
+ }
56
+ if (twoPane) {
57
+ const detailWidth = vp.columns - listWidth - 3; // gutter " │ "
58
+ const detail = (0, text_1.wrap)(vis[cursor].description, detailWidth).slice(0, rows.length);
59
+ const height = Math.max(rows.length, detail.length);
60
+ for (let i = 0; i < height; i++) {
61
+ const leftPadded = padToWidth(rows[i] ?? '', listWidth);
62
+ const right = detail[i] ?? '';
63
+ out.push(`${leftPadded} ${picocolors_1.default.dim('│')} ${picocolors_1.default.dim(right)}`);
64
+ }
65
+ }
66
+ else {
67
+ out.push(...rows);
68
+ out.push(picocolors_1.default.dim(' ' + ((0, text_1.wrap)(vis[cursor].description, vp.columns - 4)[0] ?? '')));
69
+ }
70
+ out.push(FOOTER);
71
+ return out;
72
+ }
73
+ /** Pad a possibly-colored string to `width` visible cells. */
74
+ function padToWidth(s, width) {
75
+ const pad = width - (0, text_1.displayWidth)(s);
76
+ return pad > 0 ? s + ' '.repeat(pad) : s;
77
+ }