Refine portability export behavior and skill plans

This commit is contained in:
Dotta
2026-03-14 18:59:26 -05:00
parent 7e43020a28
commit b2c0f3f9a5
13 changed files with 1126 additions and 12 deletions

View File

@@ -1,6 +1,7 @@
import { Command } from "commander";
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
import path from "node:path";
import * as p from "@clack/prompts";
import type {
Company,
CompanyPortabilityExportResult,
@@ -35,6 +36,7 @@ interface CompanyExportOptions extends BaseClientOptions {
projects?: string;
issues?: string;
projectIssues?: string;
expandReferencedSkills?: boolean;
}
interface CompanyImportOptions extends BaseClientOptions {
@@ -137,6 +139,31 @@ async function writeExportToFolder(outDir: string, exported: CompanyPortabilityE
}
}
async function confirmOverwriteExportDirectory(outDir: string): Promise<void> {
const root = path.resolve(outDir);
const stats = await stat(root).catch(() => null);
if (!stats) return;
if (!stats.isDirectory()) {
throw new Error(`Export output path ${root} exists and is not a directory.`);
}
const entries = await readdir(root);
if (entries.length === 0) return;
if (!process.stdin.isTTY || !process.stdout.isTTY) {
throw new Error(`Export output directory ${root} already contains files. Re-run interactively or choose an empty directory.`);
}
const confirmed = await p.confirm({
message: `Overwrite existing files in ${root}?`,
initialValue: false,
});
if (p.isCancel(confirmed) || !confirmed) {
throw new Error("Export cancelled.");
}
}
function matchesPrefix(company: Company, selector: string): boolean {
return company.issuePrefix.toUpperCase() === selector.toUpperCase();
}
@@ -278,6 +305,7 @@ export function registerCompanyCommands(program: Command): void {
.option("--projects <values>", "Comma-separated project shortnames/ids to export")
.option("--issues <values>", "Comma-separated issue identifiers/ids to export")
.option("--project-issues <values>", "Comma-separated project shortnames/ids whose issues should be exported")
.option("--expand-referenced-skills", "Vendor skill contents instead of exporting upstream references", false)
.action(async (companyId: string, opts: CompanyExportOptions) => {
try {
const ctx = resolveCommandContext(opts);
@@ -289,11 +317,13 @@ export function registerCompanyCommands(program: Command): void {
projects: parseCsvValues(opts.projects),
issues: parseCsvValues(opts.issues),
projectIssues: parseCsvValues(opts.projectIssues),
expandReferencedSkills: Boolean(opts.expandReferencedSkills),
},
);
if (!exported) {
throw new Error("Export request returned no data");
}
await confirmOverwriteExportDirectory(opts.out!);
await writeExportToFolder(opts.out!, exported);
printOutput(
{