@h1veframework/cli 0.5.2 → 0.7.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 +26 -24
- package/dist/index.js +101 -47
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @h1veframework/cli · `
|
|
1
|
+
# @h1veframework/cli · `h1ve`
|
|
2
2
|
|
|
3
3
|
**The command-line companion for [H1VE](https://h1ve.org) — the governance & memory layer for teams building with AI.**
|
|
4
4
|
|
|
5
5
|
Your team ships with AI (Claude Code, Cursor, Copilot). As the codebase grows, the AI and your teammates start
|
|
6
6
|
**losing context and drifting from decisions you already made**. H1VE keeps every feature aligned — a shared,
|
|
7
7
|
**eternal project memory** plus an **anti-drift engine** that checks each change against what's been decided.
|
|
8
|
-
`
|
|
8
|
+
`h1ve` drives that workflow straight from your terminal.
|
|
9
9
|
|
|
10
10
|
Part of the H1VE toolchain, alongside the **[MCP server](https://www.npmjs.com/package/@h1veframework/mcp)** for
|
|
11
11
|
Claude Code / Cursor.
|
|
@@ -16,46 +16,48 @@ Claude Code / Cursor.
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm i -g @h1veframework/cli
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
h1ve login # opens your browser → you click Authorize → done
|
|
20
|
+
h1ve status # state of the current branch's feature
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
`
|
|
23
|
+
`h1ve login` stores the credential locally (`~/.config/h1ve/credentials.json`, `600`) — the MCP server reads the
|
|
24
24
|
same one, so **one login sets up both**. The project **auto-resolves from your git remote**, so there's nothing
|
|
25
|
-
to paste and no `H1VE_PROJECT_ID` to set. `
|
|
25
|
+
to paste and no `H1VE_PROJECT_ID` to set. `h1ve logout` removes it.
|
|
26
26
|
|
|
27
|
-
> `nf
|
|
27
|
+
> The legacy `nf` command still works as an alias — every `h1ve <cmd>` below can be run as `nf <cmd>`.
|
|
28
|
+
|
|
29
|
+
> `h1ve health` saying "no snapshots yet" is **success** (you're connected; the project just has no metrics yet).
|
|
28
30
|
|
|
29
31
|
## Commands
|
|
30
32
|
|
|
31
33
|
| Command | What it does |
|
|
32
34
|
|---|---|
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
35
|
+
| `h1ve login` / `h1ve logout` | Authorize in the browser and store the credential locally / remove it |
|
|
36
|
+
| `h1ve status` | State of the current branch's feature (stage, days active, blockers, sign-offs) |
|
|
37
|
+
| `h1ve start [<#\|id>] [--slug <s>]` | Start an assigned feature: create the `feat/{you}/{slug}` branch and record the slug |
|
|
38
|
+
| `h1ve spec` | Print the feature's spec (markdown) |
|
|
39
|
+
| `h1ve move <stage> [--note]` | Move the feature to another stage |
|
|
40
|
+
| `h1ve done [--from <file>] [--no-move]` | Submit the AI declaration (JSON) and move `dev → pr` |
|
|
41
|
+
| `h1ve blocker "<desc>"` | Open a blocker on the feature (you become its owner) |
|
|
42
|
+
| `h1ve health` | Latest technical-health snapshots for the project |
|
|
43
|
+
| `h1ve connect --kind <k> --label <l> --env KEY=VAL` | Write a credential to your **local** `.env.local` and register the inventory (never sent to the server) |
|
|
44
|
+
| `h1ve serve [--port 7391]` | Run the local agent (`127.0.0.1`) so the dashboard can apply credentials from the browser |
|
|
43
45
|
|
|
44
46
|
Flags: `--json` (raw output for scripts) · `--project <name\|id>` (if you belong to more than one project) · `-h`.
|
|
45
47
|
|
|
46
48
|
## Example
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
|
-
|
|
51
|
+
h1ve start # start your assigned feature (creates the branch)
|
|
50
52
|
# ... work: git, code, commits ...
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
h1ve status # check state anytime
|
|
54
|
+
h1ve blocker "waiting on the Neon credential" # blocked? open a blocker
|
|
55
|
+
h1ve done --from ai-declaration.json # submit the AI declaration + move dev → pr
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
## CI / headless (env vars)
|
|
57
59
|
|
|
58
|
-
No browser? Configure by env (takes **precedence** over `
|
|
60
|
+
No browser? Configure by env (takes **precedence** over `h1ve login`):
|
|
59
61
|
|
|
60
62
|
```bash
|
|
61
63
|
export H1VE_API_URL="https://app.h1ve.org" # optional (this is the default)
|
|
@@ -67,12 +69,12 @@ export H1VE_API_KEY="nf_pat_..." # a PAT from app.h1ve.org/api-token
|
|
|
67
69
|
|
|
68
70
|
| Symptom | Fix |
|
|
69
71
|
|---|---|
|
|
70
|
-
| `
|
|
72
|
+
| `h1ve: command not found` | Node missing or wrong terminal. Check `node --version`. On Windows, reopen the shell or use the VS Code terminal. |
|
|
71
73
|
| `404` on install | npm propagation right after a publish — wait a few minutes. |
|
|
72
74
|
| `NO_PROJECT` | You belong to more than one project — pass `--project <name\|id>`. |
|
|
73
75
|
| `401` / `403` | Wrong/missing token. Make sure `H1VE_API_KEY` is a PAT (`nf_pat_…`). `403 SERVICE_CANNOT_WRITE` = a read-only service key used on a write → use a PAT. |
|
|
74
76
|
|
|
75
|
-
Requires **Node.js 18.18+**.
|
|
77
|
+
Requires **Node.js 18.18+**. The legacy `nf` command and env names `NEXUS_FLOW_API_URL` / `NEXUS_FLOW_API_KEY` are still accepted.
|
|
76
78
|
|
|
77
79
|
---
|
|
78
80
|
|
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 {
|
|
@@ -77,7 +95,7 @@ function errorForStatus(status2, code) {
|
|
|
77
95
|
case 422:
|
|
78
96
|
return new NexusApiError("INVALID_INPUT", "Branch inv\xE1lida.");
|
|
79
97
|
default:
|
|
80
|
-
return new NexusApiError("API_ERROR", `Falha ao consultar o
|
|
98
|
+
return new NexusApiError("API_ERROR", `Falha ao consultar o H1VE (HTTP ${status2}).`);
|
|
81
99
|
}
|
|
82
100
|
}
|
|
83
101
|
function mapWriteError(status2, code) {
|
|
@@ -106,7 +124,7 @@ function mapWriteError(status2, code) {
|
|
|
106
124
|
case 422:
|
|
107
125
|
return new NexusApiError(code ?? "INVALID_INPUT", "Entrada inv\xE1lida para esta a\xE7\xE3o.");
|
|
108
126
|
default:
|
|
109
|
-
return new NexusApiError("API_ERROR", `Falha ao escrever no
|
|
127
|
+
return new NexusApiError("API_ERROR", `Falha ao escrever no H1VE (HTTP ${status2}).`);
|
|
110
128
|
}
|
|
111
129
|
}
|
|
112
130
|
async function readErrorCode(res) {
|
|
@@ -416,7 +434,7 @@ async function resolveConfig(env = process.env, store = defaultCredentialStore)
|
|
|
416
434
|
}
|
|
417
435
|
if (!apiKey) {
|
|
418
436
|
throw new CliError(
|
|
419
|
-
"Voc\xEA n\xE3o est\xE1 logado. Rode `
|
|
437
|
+
"Voc\xEA n\xE3o est\xE1 logado. Rode `h1ve login` (abre o navegador) \u2014 ou defina H1VE_API_KEY.\n (o nome legado NEXUS_FLOW_API_KEY tamb\xE9m \xE9 aceito)",
|
|
420
438
|
2
|
|
421
439
|
);
|
|
422
440
|
}
|
|
@@ -472,17 +490,45 @@ 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: h1ve 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
|
-
|
|
500
|
+
var PRE_DEV_STAGES = /* @__PURE__ */ new Set(["backlog", "spec", "blocked"]);
|
|
501
|
+
function startFlowLines(r) {
|
|
502
|
+
const stage = r.landed_stage ?? r.feature.stage;
|
|
503
|
+
if (!PRE_DEV_STAGES.has(stage)) {
|
|
504
|
+
return [
|
|
505
|
+
`Fluxo: card em ${stageLabel(stage)} \u2014 pronto para codar.`,
|
|
506
|
+
"Pr\xF3ximo: codar \u2192 'h1ve status' \u2192 'h1ve done' (move dev \u2192 pr)"
|
|
507
|
+
];
|
|
508
|
+
}
|
|
509
|
+
const blocked = r.advance?.blocked_by;
|
|
510
|
+
if (blocked === "SPEC_REQUIRED" || blocked === "SPEC_MALFORMED") {
|
|
511
|
+
return [
|
|
512
|
+
"Fluxo: card em Spec (aguarda a SPEC \u2014 Modo Estrito).",
|
|
513
|
+
"Pr\xF3ximo: escreva a SPEC no card \u2192 'h1ve start' de novo para avan\xE7ar at\xE9 Dev."
|
|
514
|
+
];
|
|
515
|
+
}
|
|
516
|
+
if (blocked === "INVALID_TRANSITION") {
|
|
517
|
+
return [
|
|
518
|
+
`Fluxo: card em ${stageLabel(stage)}.`,
|
|
519
|
+
"Pr\xF3ximo: pe\xE7a ao arquiteto para mover o card at\xE9 Dev (voc\xEA n\xE3o tem permiss\xE3o)."
|
|
520
|
+
];
|
|
521
|
+
}
|
|
522
|
+
return [
|
|
523
|
+
`Fluxo: card em ${stageLabel(stage)}.`,
|
|
524
|
+
"Pr\xF3ximo: mova o card at\xE9 Dev no board para come\xE7ar."
|
|
525
|
+
];
|
|
526
|
+
}
|
|
527
|
+
function formatStart(r, opts) {
|
|
482
528
|
return [
|
|
483
|
-
`\u2713 Iniciada: ${r.feature.name}`,
|
|
484
|
-
`Branch: ${r.branch_name} (criada e ativa)`,
|
|
485
|
-
|
|
529
|
+
`\u2713 ${opts?.resumed ? "Retomada" : "Iniciada"}: ${r.feature.name}`,
|
|
530
|
+
`Branch: ${r.branch_name} (${opts?.resumed ? "pronta e ativa" : "criada e ativa"})`,
|
|
531
|
+
...startFlowLines(r)
|
|
486
532
|
].join("\n");
|
|
487
533
|
}
|
|
488
534
|
function formatHealth(snaps) {
|
|
@@ -538,7 +584,7 @@ function isStage(s) {
|
|
|
538
584
|
}
|
|
539
585
|
async function move(io, args) {
|
|
540
586
|
const stage = args.positional;
|
|
541
|
-
if (!stage) throw new CliError("Uso:
|
|
587
|
+
if (!stage) throw new CliError("Uso: h1ve move <stage>");
|
|
542
588
|
if (!isStage(stage)) {
|
|
543
589
|
throw new CliError(`Stage inv\xE1lido: ${stage}. V\xE1lidos: ${STAGES.join(", ")}`);
|
|
544
590
|
}
|
|
@@ -551,7 +597,7 @@ async function move(io, args) {
|
|
|
551
597
|
// src/commands/blocker.ts
|
|
552
598
|
async function blocker(io, args) {
|
|
553
599
|
const description = args.positional;
|
|
554
|
-
if (!description) throw new CliError('Uso:
|
|
600
|
+
if (!description) throw new CliError('Uso: h1ve blocker "<descri\xE7\xE3o>"');
|
|
555
601
|
const branch = await io.resolveBranch();
|
|
556
602
|
const digest = await io.client.getContext(branch);
|
|
557
603
|
const result = await io.client.createBlocker(digest.feature.id, description);
|
|
@@ -591,7 +637,7 @@ ${movedLine}`, data: { declaration, move: moved } };
|
|
|
591
637
|
throw new CliError(
|
|
592
638
|
`${sent}
|
|
593
639
|
\u2717 Move dev \u2192 pr falhou: ${reason}
|
|
594
|
-
(a declaration J\xC1 foi enviada \u2014 rode \`
|
|
640
|
+
(a declaration J\xC1 foi enviada \u2014 rode \`h1ve move pr\` para concluir; N\xC3O repita \`h1ve done\`, pois enviaria a declaration de novo.)`
|
|
595
641
|
);
|
|
596
642
|
}
|
|
597
643
|
}
|
|
@@ -603,8 +649,15 @@ async function health(io) {
|
|
|
603
649
|
}
|
|
604
650
|
|
|
605
651
|
// src/commands/start.ts
|
|
606
|
-
function
|
|
607
|
-
return
|
|
652
|
+
function branchSlugOf(branch) {
|
|
653
|
+
return branch.match(/^feat\/[^/]+\/(.+)$/)?.[1] ?? null;
|
|
654
|
+
}
|
|
655
|
+
function startable(features, localBranches) {
|
|
656
|
+
const localSlugs = new Set(
|
|
657
|
+
localBranches.map(branchSlugOf).filter((s) => s !== null)
|
|
658
|
+
);
|
|
659
|
+
const hasLocalBranch = (f) => f.branch_slug !== null && localSlugs.has(f.branch_slug);
|
|
660
|
+
return features.filter((f) => f.stage !== "main" && !hasLocalBranch(f));
|
|
608
661
|
}
|
|
609
662
|
function pick(list, arg) {
|
|
610
663
|
if (!arg) return list.length === 1 ? list[0] ?? null : null;
|
|
@@ -612,30 +665,28 @@ function pick(list, arg) {
|
|
|
612
665
|
if (byId) return byId;
|
|
613
666
|
const idx = Number(arg);
|
|
614
667
|
if (Number.isInteger(idx) && idx >= 1 && idx <= list.length) return list[idx - 1] ?? null;
|
|
615
|
-
throw new CliError(`Feature n\xE3o encontrada: ${arg}. Rode '
|
|
668
|
+
throw new CliError(`Feature n\xE3o encontrada: ${arg}. Rode 'h1ve start' para ver a lista.`);
|
|
616
669
|
}
|
|
617
670
|
async function start(io, args) {
|
|
618
|
-
const
|
|
671
|
+
const [features, localBranches] = await Promise.all([io.client.listFeatures(), io.listBranches()]);
|
|
672
|
+
const list = startable(features, localBranches);
|
|
619
673
|
if (list.length === 0) {
|
|
620
|
-
throw new CliError("Nenhuma feature
|
|
674
|
+
throw new CliError("Nenhuma feature para iniciar ou retomar (sem branch local e fora de main).");
|
|
621
675
|
}
|
|
622
676
|
const selected = pick(list, args.positional);
|
|
623
677
|
if (!selected) {
|
|
624
678
|
return { human: formatStartList(list), data: { features: list } };
|
|
625
679
|
}
|
|
680
|
+
const resuming = selected.branch_slug !== null;
|
|
626
681
|
const result = await io.client.startFeature(selected.id, args.slug);
|
|
627
682
|
try {
|
|
628
683
|
await io.createBranch(result.branch_name);
|
|
629
684
|
} catch (err) {
|
|
630
685
|
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;
|
|
686
|
+
if (code === "BRANCH_EXISTS") await io.switchBranch(result.branch_name);
|
|
687
|
+
else throw err;
|
|
637
688
|
}
|
|
638
|
-
return { human: formatStart(result), data: result };
|
|
689
|
+
return { human: formatStart(result, { resumed: resuming }), data: result };
|
|
639
690
|
}
|
|
640
691
|
|
|
641
692
|
// src/commands/connect.ts
|
|
@@ -1212,7 +1263,7 @@ async function startCallbackServer(expectedState) {
|
|
|
1212
1263
|
const waitForCode = () => new Promise((resolve, reject) => {
|
|
1213
1264
|
const timer = setTimeout(() => {
|
|
1214
1265
|
server.close();
|
|
1215
|
-
reject(new CliError("Tempo esgotado esperando a autoriza\xE7\xE3o. Rode `
|
|
1266
|
+
reject(new CliError("Tempo esgotado esperando a autoriza\xE7\xE3o. Rode `h1ve login` de novo."));
|
|
1216
1267
|
}, LOGIN_TIMEOUT_MS);
|
|
1217
1268
|
void codePromise.then((code) => {
|
|
1218
1269
|
clearTimeout(timer);
|
|
@@ -1262,31 +1313,32 @@ async function logout(io, _args) {
|
|
|
1262
1313
|
}
|
|
1263
1314
|
|
|
1264
1315
|
// src/index.ts
|
|
1265
|
-
var VERSION = "0.
|
|
1266
|
-
var HELP = `
|
|
1316
|
+
var VERSION = "0.7.0";
|
|
1317
|
+
var HELP = `h1ve \u2014 CLI do H1VE
|
|
1318
|
+
(o comando legado \`nf\` continua funcionando como alias)
|
|
1267
1319
|
|
|
1268
1320
|
Uso:
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
inicia uma feature
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1321
|
+
h1ve login autoriza no navegador e salva a credencial local (sem colar nada)
|
|
1322
|
+
h1ve logout remove a credencial local
|
|
1323
|
+
h1ve start [<n\xBA|id>] [--slug <s>]
|
|
1324
|
+
inicia (ou retoma) uma feature: cria/entra na branch e grava o slug
|
|
1325
|
+
h1ve status estado da feature da branch atual
|
|
1326
|
+
h1ve spec imprime a spec da feature
|
|
1327
|
+
h1ve move <stage> [--note] move a feature de stage
|
|
1328
|
+
h1ve blocker "<desc>" abre um blocker na feature
|
|
1329
|
+
h1ve done [--from <arq>] [--no-move]
|
|
1278
1330
|
envia a AI declaration (JSON) e move dev \u2192 pr
|
|
1279
|
-
|
|
1280
|
-
|
|
1331
|
+
h1ve health \xFAltimos snapshots de sa\xFAde t\xE9cnica
|
|
1332
|
+
h1ve connect --kind <k> --label <l> --env KEY=VAL [...]
|
|
1281
1333
|
aplica a credencial no .env.local LOCAL (nunca ao servidor)
|
|
1282
1334
|
e registra o invent\xE1rio da conex\xE3o (Jeito B)
|
|
1283
|
-
|
|
1335
|
+
h1ve serve [--port 7391] [--origin http://localhost:3000]
|
|
1284
1336
|
sobe o agente local (127.0.0.1) p/ o menu visual aplicar
|
|
1285
1337
|
credenciais pelo navegador \u2014 imprime o c\xF3digo de pareamento
|
|
1286
1338
|
|
|
1287
1339
|
Flags:
|
|
1288
1340
|
--json sa\xEDda em JSON cru (comp\xF5e em scripts)
|
|
1289
|
-
--slug <s> slug custom para '
|
|
1341
|
+
--slug <s> slug custom para 'h1ve start' (default = derivado do nome)
|
|
1290
1342
|
--kind <k> tipo da conex\xE3o (database|deploy|observability|api|auth|other)
|
|
1291
1343
|
--label <l> nome da conex\xE3o (ex.: "Neon (prod)")
|
|
1292
1344
|
--env KEY=VAL vari\xE1vel a escrever no .env.local (repet\xEDvel)
|
|
@@ -1295,7 +1347,7 @@ Flags:
|
|
|
1295
1347
|
-h, --help esta ajuda
|
|
1296
1348
|
-v, --version vers\xE3o do CLI
|
|
1297
1349
|
|
|
1298
|
-
Ambiente (opcional \u2014 '
|
|
1350
|
+
Ambiente (opcional \u2014 'h1ve login' dispensa):
|
|
1299
1351
|
H1VE_API_URL URL do H1VE Flow (default https://app.h1ve.org)
|
|
1300
1352
|
H1VE_API_KEY PAT (nf_pat_\u2026) p/ escrever, ou a chave de servi\xE7o (s\xF3 leitura)
|
|
1301
1353
|
(nomes legados aceitos: NEXUS_FLOW_API_URL / NEXUS_FLOW_API_KEY)
|
|
@@ -1321,7 +1373,7 @@ function parse(argv) {
|
|
|
1321
1373
|
json: { type: "boolean" },
|
|
1322
1374
|
help: { type: "boolean", short: "h" },
|
|
1323
1375
|
version: { type: "boolean", short: "v" },
|
|
1324
|
-
//
|
|
1376
|
+
// h1ve connect (SPEC-050)
|
|
1325
1377
|
kind: { type: "string" },
|
|
1326
1378
|
label: { type: "string" },
|
|
1327
1379
|
url: { type: "string" },
|
|
@@ -1329,13 +1381,13 @@ function parse(argv) {
|
|
|
1329
1381
|
project: { type: "string" },
|
|
1330
1382
|
file: { type: "string" },
|
|
1331
1383
|
env: { type: "string", multiple: true },
|
|
1332
|
-
//
|
|
1384
|
+
// h1ve serve (SPEC-051)
|
|
1333
1385
|
port: { type: "string" },
|
|
1334
1386
|
origin: { type: "string" }
|
|
1335
1387
|
}
|
|
1336
1388
|
});
|
|
1337
1389
|
} catch {
|
|
1338
|
-
throw new CliError("Argumentos inv\xE1lidos. Veja `
|
|
1390
|
+
throw new CliError("Argumentos inv\xE1lidos. Veja `h1ve --help`.", 2);
|
|
1339
1391
|
}
|
|
1340
1392
|
}
|
|
1341
1393
|
async function main(argv) {
|
|
@@ -1363,6 +1415,8 @@ ${HELP}`);
|
|
|
1363
1415
|
client,
|
|
1364
1416
|
resolveBranch: () => currentBranch(defaultGitRunner),
|
|
1365
1417
|
createBranch: (name) => createBranch(name, defaultGitRunner),
|
|
1418
|
+
switchBranch: (name) => switchBranch(name, defaultGitRunner),
|
|
1419
|
+
listBranches: () => listLocalBranches(defaultGitRunner),
|
|
1366
1420
|
readFile: (path) => readFile(path, "utf8"),
|
|
1367
1421
|
readStdin,
|
|
1368
1422
|
writeEnvVars: (pairs, opts) => writeEnvVars(pairs, { file: opts?.file }),
|
|
@@ -1397,7 +1451,7 @@ function messageFor(err) {
|
|
|
1397
1451
|
if (err instanceof Error && (err.message === "NOT_A_GIT_REPO" || err.message === "GIT_BRANCH_FAILED")) {
|
|
1398
1452
|
return "N\xE3o consegui criar a branch git. Rode dentro do reposit\xF3rio (git) da feature.";
|
|
1399
1453
|
}
|
|
1400
|
-
return "Erro inesperado ao falar com o
|
|
1454
|
+
return "Erro inesperado ao falar com o H1VE.";
|
|
1401
1455
|
}
|
|
1402
1456
|
main(process.argv.slice(2)).then((code) => {
|
|
1403
1457
|
process.exitCode = code;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h1veframework/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
7
7
|
"bin": {
|
|
8
|
+
"h1ve": "dist/index.js",
|
|
8
9
|
"nf": "dist/index.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|