@h1veframework/cli 0.5.2 → 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/index.js +46 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50,6 +50,24 @@ async function createBranch(name, runner = defaultGitRunner) {
|
|
|
50
50
|
throw new Error("GIT_BRANCH_FAILED");
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
async function switchBranch(name, runner = defaultGitRunner) {
|
|
54
|
+
try {
|
|
55
|
+
await runner(["switch", name]);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const text = gitErrText(err);
|
|
58
|
+
if (/not a git repository/i.test(text)) throw new Error("NOT_A_GIT_REPO");
|
|
59
|
+
throw new Error("GIT_SWITCH_FAILED");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function listLocalBranches(runner = defaultGitRunner) {
|
|
63
|
+
let out;
|
|
64
|
+
try {
|
|
65
|
+
out = await runner(["for-each-ref", "--format=%(refname:short)", "refs/heads"]);
|
|
66
|
+
} catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
70
|
+
}
|
|
53
71
|
|
|
54
72
|
// ../core/src/client.ts
|
|
55
73
|
var NexusApiError = class extends Error {
|
|
@@ -472,16 +490,17 @@ function formatBlocker(r) {
|
|
|
472
490
|
return `\u2713 Blocker aberto: ${r.description}`;
|
|
473
491
|
}
|
|
474
492
|
function formatStartList(list) {
|
|
475
|
-
const header = "Features
|
|
476
|
-
const rows = list.map(
|
|
477
|
-
|
|
478
|
-
)
|
|
493
|
+
const header = "Features para iniciar ou retomar \u2014 escolha: nf start <n\xBA>";
|
|
494
|
+
const rows = list.map((f, i) => {
|
|
495
|
+
const tag = f.branch_slug !== null ? " (retomar)" : "";
|
|
496
|
+
return ` ${String(i + 1).padStart(2)}. ${f.name} [${stageLabel(f.stage)}]${tag}`;
|
|
497
|
+
});
|
|
479
498
|
return [header, ...rows].join("\n");
|
|
480
499
|
}
|
|
481
|
-
function formatStart(r) {
|
|
500
|
+
function formatStart(r, opts) {
|
|
482
501
|
return [
|
|
483
|
-
`\u2713 Iniciada: ${r.feature.name}`,
|
|
484
|
-
`Branch: ${r.branch_name} (criada e ativa)`,
|
|
502
|
+
`\u2713 ${opts?.resumed ? "Retomada" : "Iniciada"}: ${r.feature.name}`,
|
|
503
|
+
`Branch: ${r.branch_name} (${opts?.resumed ? "pronta e ativa" : "criada e ativa"})`,
|
|
485
504
|
"Pr\xF3ximo: codar \u2192 'nf status' \u2192 'nf done' (move dev \u2192 pr)"
|
|
486
505
|
].join("\n");
|
|
487
506
|
}
|
|
@@ -603,8 +622,15 @@ async function health(io) {
|
|
|
603
622
|
}
|
|
604
623
|
|
|
605
624
|
// src/commands/start.ts
|
|
606
|
-
function
|
|
607
|
-
return
|
|
625
|
+
function branchSlugOf(branch) {
|
|
626
|
+
return branch.match(/^feat\/[^/]+\/(.+)$/)?.[1] ?? null;
|
|
627
|
+
}
|
|
628
|
+
function startable(features, localBranches) {
|
|
629
|
+
const localSlugs = new Set(
|
|
630
|
+
localBranches.map(branchSlugOf).filter((s) => s !== null)
|
|
631
|
+
);
|
|
632
|
+
const hasLocalBranch = (f) => f.branch_slug !== null && localSlugs.has(f.branch_slug);
|
|
633
|
+
return features.filter((f) => f.stage !== "main" && !hasLocalBranch(f));
|
|
608
634
|
}
|
|
609
635
|
function pick(list, arg) {
|
|
610
636
|
if (!arg) return list.length === 1 ? list[0] ?? null : null;
|
|
@@ -615,27 +641,25 @@ function pick(list, arg) {
|
|
|
615
641
|
throw new CliError(`Feature n\xE3o encontrada: ${arg}. Rode 'nf start' para ver a lista.`);
|
|
616
642
|
}
|
|
617
643
|
async function start(io, args) {
|
|
618
|
-
const
|
|
644
|
+
const [features, localBranches] = await Promise.all([io.client.listFeatures(), io.listBranches()]);
|
|
645
|
+
const list = startable(features, localBranches);
|
|
619
646
|
if (list.length === 0) {
|
|
620
|
-
throw new CliError("Nenhuma feature
|
|
647
|
+
throw new CliError("Nenhuma feature para iniciar ou retomar (sem branch local e fora de main).");
|
|
621
648
|
}
|
|
622
649
|
const selected = pick(list, args.positional);
|
|
623
650
|
if (!selected) {
|
|
624
651
|
return { human: formatStartList(list), data: { features: list } };
|
|
625
652
|
}
|
|
653
|
+
const resuming = selected.branch_slug !== null;
|
|
626
654
|
const result = await io.client.startFeature(selected.id, args.slug);
|
|
627
655
|
try {
|
|
628
656
|
await io.createBranch(result.branch_name);
|
|
629
657
|
} catch (err) {
|
|
630
658
|
const code = err instanceof Error ? err.message : "";
|
|
631
|
-
if (code === "BRANCH_EXISTS")
|
|
632
|
-
|
|
633
|
-
`branch_slug gravado, mas a branch '${result.branch_name}' j\xE1 existe. Rode: git switch ${result.branch_name}`
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
throw err;
|
|
659
|
+
if (code === "BRANCH_EXISTS") await io.switchBranch(result.branch_name);
|
|
660
|
+
else throw err;
|
|
637
661
|
}
|
|
638
|
-
return { human: formatStart(result), data: result };
|
|
662
|
+
return { human: formatStart(result, { resumed: resuming }), data: result };
|
|
639
663
|
}
|
|
640
664
|
|
|
641
665
|
// src/commands/connect.ts
|
|
@@ -1262,14 +1286,14 @@ async function logout(io, _args) {
|
|
|
1262
1286
|
}
|
|
1263
1287
|
|
|
1264
1288
|
// src/index.ts
|
|
1265
|
-
var VERSION = "0.
|
|
1289
|
+
var VERSION = "0.6.0";
|
|
1266
1290
|
var HELP = `nf \u2014 CLI do Nexus Flow
|
|
1267
1291
|
|
|
1268
1292
|
Uso:
|
|
1269
1293
|
nf login autoriza no navegador e salva a credencial local (sem colar nada)
|
|
1270
1294
|
nf logout remove a credencial local
|
|
1271
1295
|
nf start [<n\xBA|id>] [--slug <s>]
|
|
1272
|
-
inicia uma feature
|
|
1296
|
+
inicia (ou retoma) uma feature: cria/entra na branch e grava o slug
|
|
1273
1297
|
nf status estado da feature da branch atual
|
|
1274
1298
|
nf spec imprime a spec da feature
|
|
1275
1299
|
nf move <stage> [--note] move a feature de stage
|
|
@@ -1363,6 +1387,8 @@ ${HELP}`);
|
|
|
1363
1387
|
client,
|
|
1364
1388
|
resolveBranch: () => currentBranch(defaultGitRunner),
|
|
1365
1389
|
createBranch: (name) => createBranch(name, defaultGitRunner),
|
|
1390
|
+
switchBranch: (name) => switchBranch(name, defaultGitRunner),
|
|
1391
|
+
listBranches: () => listLocalBranches(defaultGitRunner),
|
|
1366
1392
|
readFile: (path) => readFile(path, "utf8"),
|
|
1367
1393
|
readStdin,
|
|
1368
1394
|
writeEnvVars: (pairs, opts) => writeEnvVars(pairs, { file: opts?.file }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h1veframework/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The CLI for H1VE — the governance & memory layer for teams building with AI. Drive your feature flow (start, status, move, spec, done) from the terminal. Works with Claude Code, Cursor and Copilot.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|