@h1veframework/cli 0.6.0 → 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.
Files changed (3) hide show
  1. package/README.md +26 -24
  2. package/dist/index.js +57 -29
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # @h1veframework/cli · `nf`
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
- `nf` drives that workflow straight from your terminal.
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
- nf login # opens your browser → you click Authorize → done
20
- nf status # state of the current branch's feature
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
- `nf login` stores the credential locally (`~/.config/h1ve/credentials.json`, `600`) — the MCP server reads the
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. `nf logout` removes it.
25
+ to paste and no `H1VE_PROJECT_ID` to set. `h1ve logout` removes it.
26
26
 
27
- > `nf health` saying "no snapshots yet" is **success** (you're connected; the project just has no metrics yet).
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
- | `nf login` / `nf logout` | Authorize in the browser and store the credential locally / remove it |
34
- | `nf status` | State of the current branch's feature (stage, days active, blockers, sign-offs) |
35
- | `nf start [<#\|id>] [--slug <s>]` | Start an assigned feature: create the `feat/{you}/{slug}` branch and record the slug |
36
- | `nf spec` | Print the feature's spec (markdown) |
37
- | `nf move <stage> [--note]` | Move the feature to another stage |
38
- | `nf done [--from <file>] [--no-move]` | Submit the AI declaration (JSON) and move `dev → pr` |
39
- | `nf blocker "<desc>"` | Open a blocker on the feature (you become its owner) |
40
- | `nf health` | Latest technical-health snapshots for the project |
41
- | `nf 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) |
42
- | `nf serve [--port 7391]` | Run the local agent (`127.0.0.1`) so the dashboard can apply credentials from the browser |
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
- nf start # start your assigned feature (creates the branch)
51
+ h1ve start # start your assigned feature (creates the branch)
50
52
  # ... work: git, code, commits ...
51
- nf status # check state anytime
52
- nf blocker "waiting on the Neon credential" # blocked? open a blocker
53
- nf done --from ai-declaration.json # submit the AI declaration + move dev → pr
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 `nf login`):
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
- | `nf: command not found` | Node missing or wrong terminal. Check `node --version`. On Windows, reopen the shell or use the VS Code terminal. |
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+**. Legacy env names `NEXUS_FLOW_API_URL` / `NEXUS_FLOW_API_KEY` are still accepted.
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
@@ -95,7 +95,7 @@ function errorForStatus(status2, code) {
95
95
  case 422:
96
96
  return new NexusApiError("INVALID_INPUT", "Branch inv\xE1lida.");
97
97
  default:
98
- return new NexusApiError("API_ERROR", `Falha ao consultar o Nexus Flow (HTTP ${status2}).`);
98
+ return new NexusApiError("API_ERROR", `Falha ao consultar o H1VE (HTTP ${status2}).`);
99
99
  }
100
100
  }
101
101
  function mapWriteError(status2, code) {
@@ -124,7 +124,7 @@ function mapWriteError(status2, code) {
124
124
  case 422:
125
125
  return new NexusApiError(code ?? "INVALID_INPUT", "Entrada inv\xE1lida para esta a\xE7\xE3o.");
126
126
  default:
127
- return new NexusApiError("API_ERROR", `Falha ao escrever no Nexus Flow (HTTP ${status2}).`);
127
+ return new NexusApiError("API_ERROR", `Falha ao escrever no H1VE (HTTP ${status2}).`);
128
128
  }
129
129
  }
130
130
  async function readErrorCode(res) {
@@ -434,7 +434,7 @@ async function resolveConfig(env = process.env, store = defaultCredentialStore)
434
434
  }
435
435
  if (!apiKey) {
436
436
  throw new CliError(
437
- "Voc\xEA n\xE3o est\xE1 logado. Rode `nf login` (abre o navegador) \u2014 ou defina H1VE_API_KEY.\n (o nome legado NEXUS_FLOW_API_KEY tamb\xE9m \xE9 aceito)",
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)",
438
438
  2
439
439
  );
440
440
  }
@@ -490,18 +490,45 @@ function formatBlocker(r) {
490
490
  return `\u2713 Blocker aberto: ${r.description}`;
491
491
  }
492
492
  function formatStartList(list) {
493
- const header = "Features para iniciar ou retomar \u2014 escolha: nf start <n\xBA>";
493
+ const header = "Features para iniciar ou retomar \u2014 escolha: h1ve start <n\xBA>";
494
494
  const rows = list.map((f, i) => {
495
495
  const tag = f.branch_slug !== null ? " (retomar)" : "";
496
496
  return ` ${String(i + 1).padStart(2)}. ${f.name} [${stageLabel(f.stage)}]${tag}`;
497
497
  });
498
498
  return [header, ...rows].join("\n");
499
499
  }
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
+ }
500
527
  function formatStart(r, opts) {
501
528
  return [
502
529
  `\u2713 ${opts?.resumed ? "Retomada" : "Iniciada"}: ${r.feature.name}`,
503
530
  `Branch: ${r.branch_name} (${opts?.resumed ? "pronta e ativa" : "criada e ativa"})`,
504
- "Pr\xF3ximo: codar \u2192 'nf status' \u2192 'nf done' (move dev \u2192 pr)"
531
+ ...startFlowLines(r)
505
532
  ].join("\n");
506
533
  }
507
534
  function formatHealth(snaps) {
@@ -557,7 +584,7 @@ function isStage(s) {
557
584
  }
558
585
  async function move(io, args) {
559
586
  const stage = args.positional;
560
- if (!stage) throw new CliError("Uso: nf move <stage>");
587
+ if (!stage) throw new CliError("Uso: h1ve move <stage>");
561
588
  if (!isStage(stage)) {
562
589
  throw new CliError(`Stage inv\xE1lido: ${stage}. V\xE1lidos: ${STAGES.join(", ")}`);
563
590
  }
@@ -570,7 +597,7 @@ async function move(io, args) {
570
597
  // src/commands/blocker.ts
571
598
  async function blocker(io, args) {
572
599
  const description = args.positional;
573
- if (!description) throw new CliError('Uso: nf blocker "<descri\xE7\xE3o>"');
600
+ if (!description) throw new CliError('Uso: h1ve blocker "<descri\xE7\xE3o>"');
574
601
  const branch = await io.resolveBranch();
575
602
  const digest = await io.client.getContext(branch);
576
603
  const result = await io.client.createBlocker(digest.feature.id, description);
@@ -610,7 +637,7 @@ ${movedLine}`, data: { declaration, move: moved } };
610
637
  throw new CliError(
611
638
  `${sent}
612
639
  \u2717 Move dev \u2192 pr falhou: ${reason}
613
- (a declaration J\xC1 foi enviada \u2014 rode \`nf move pr\` para concluir; N\xC3O repita \`nf done\`, pois enviaria a declaration de novo.)`
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.)`
614
641
  );
615
642
  }
616
643
  }
@@ -638,7 +665,7 @@ function pick(list, arg) {
638
665
  if (byId) return byId;
639
666
  const idx = Number(arg);
640
667
  if (Number.isInteger(idx) && idx >= 1 && idx <= list.length) return list[idx - 1] ?? null;
641
- throw new CliError(`Feature n\xE3o encontrada: ${arg}. Rode 'nf start' para ver a lista.`);
668
+ throw new CliError(`Feature n\xE3o encontrada: ${arg}. Rode 'h1ve start' para ver a lista.`);
642
669
  }
643
670
  async function start(io, args) {
644
671
  const [features, localBranches] = await Promise.all([io.client.listFeatures(), io.listBranches()]);
@@ -1236,7 +1263,7 @@ async function startCallbackServer(expectedState) {
1236
1263
  const waitForCode = () => new Promise((resolve, reject) => {
1237
1264
  const timer = setTimeout(() => {
1238
1265
  server.close();
1239
- reject(new CliError("Tempo esgotado esperando a autoriza\xE7\xE3o. Rode `nf login` de novo."));
1266
+ reject(new CliError("Tempo esgotado esperando a autoriza\xE7\xE3o. Rode `h1ve login` de novo."));
1240
1267
  }, LOGIN_TIMEOUT_MS);
1241
1268
  void codePromise.then((code) => {
1242
1269
  clearTimeout(timer);
@@ -1286,31 +1313,32 @@ async function logout(io, _args) {
1286
1313
  }
1287
1314
 
1288
1315
  // src/index.ts
1289
- var VERSION = "0.6.0";
1290
- var HELP = `nf \u2014 CLI do Nexus Flow
1316
+ var VERSION = "0.7.0";
1317
+ var HELP = `h1ve \u2014 CLI do H1VE
1318
+ (o comando legado \`nf\` continua funcionando como alias)
1291
1319
 
1292
1320
  Uso:
1293
- nf login autoriza no navegador e salva a credencial local (sem colar nada)
1294
- nf logout remove a credencial local
1295
- nf start [<n\xBA|id>] [--slug <s>]
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>]
1296
1324
  inicia (ou retoma) uma feature: cria/entra na branch e grava o slug
1297
- nf status estado da feature da branch atual
1298
- nf spec imprime a spec da feature
1299
- nf move <stage> [--note] move a feature de stage
1300
- nf blocker "<desc>" abre um blocker na feature
1301
- nf done [--from <arq>] [--no-move]
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]
1302
1330
  envia a AI declaration (JSON) e move dev \u2192 pr
1303
- nf health \xFAltimos snapshots de sa\xFAde t\xE9cnica
1304
- nf connect --kind <k> --label <l> --env KEY=VAL [...]
1331
+ h1ve health \xFAltimos snapshots de sa\xFAde t\xE9cnica
1332
+ h1ve connect --kind <k> --label <l> --env KEY=VAL [...]
1305
1333
  aplica a credencial no .env.local LOCAL (nunca ao servidor)
1306
1334
  e registra o invent\xE1rio da conex\xE3o (Jeito B)
1307
- nf serve [--port 7391] [--origin http://localhost:3000]
1335
+ h1ve serve [--port 7391] [--origin http://localhost:3000]
1308
1336
  sobe o agente local (127.0.0.1) p/ o menu visual aplicar
1309
1337
  credenciais pelo navegador \u2014 imprime o c\xF3digo de pareamento
1310
1338
 
1311
1339
  Flags:
1312
1340
  --json sa\xEDda em JSON cru (comp\xF5e em scripts)
1313
- --slug <s> slug custom para 'nf start' (default = derivado do nome)
1341
+ --slug <s> slug custom para 'h1ve start' (default = derivado do nome)
1314
1342
  --kind <k> tipo da conex\xE3o (database|deploy|observability|api|auth|other)
1315
1343
  --label <l> nome da conex\xE3o (ex.: "Neon (prod)")
1316
1344
  --env KEY=VAL vari\xE1vel a escrever no .env.local (repet\xEDvel)
@@ -1319,7 +1347,7 @@ Flags:
1319
1347
  -h, --help esta ajuda
1320
1348
  -v, --version vers\xE3o do CLI
1321
1349
 
1322
- Ambiente (opcional \u2014 'nf login' dispensa):
1350
+ Ambiente (opcional \u2014 'h1ve login' dispensa):
1323
1351
  H1VE_API_URL URL do H1VE Flow (default https://app.h1ve.org)
1324
1352
  H1VE_API_KEY PAT (nf_pat_\u2026) p/ escrever, ou a chave de servi\xE7o (s\xF3 leitura)
1325
1353
  (nomes legados aceitos: NEXUS_FLOW_API_URL / NEXUS_FLOW_API_KEY)
@@ -1345,7 +1373,7 @@ function parse(argv) {
1345
1373
  json: { type: "boolean" },
1346
1374
  help: { type: "boolean", short: "h" },
1347
1375
  version: { type: "boolean", short: "v" },
1348
- // nf connect (SPEC-050)
1376
+ // h1ve connect (SPEC-050)
1349
1377
  kind: { type: "string" },
1350
1378
  label: { type: "string" },
1351
1379
  url: { type: "string" },
@@ -1353,13 +1381,13 @@ function parse(argv) {
1353
1381
  project: { type: "string" },
1354
1382
  file: { type: "string" },
1355
1383
  env: { type: "string", multiple: true },
1356
- // nf serve (SPEC-051)
1384
+ // h1ve serve (SPEC-051)
1357
1385
  port: { type: "string" },
1358
1386
  origin: { type: "string" }
1359
1387
  }
1360
1388
  });
1361
1389
  } catch {
1362
- throw new CliError("Argumentos inv\xE1lidos. Veja `nf --help`.", 2);
1390
+ throw new CliError("Argumentos inv\xE1lidos. Veja `h1ve --help`.", 2);
1363
1391
  }
1364
1392
  }
1365
1393
  async function main(argv) {
@@ -1423,7 +1451,7 @@ function messageFor(err) {
1423
1451
  if (err instanceof Error && (err.message === "NOT_A_GIT_REPO" || err.message === "GIT_BRANCH_FAILED")) {
1424
1452
  return "N\xE3o consegui criar a branch git. Rode dentro do reposit\xF3rio (git) da feature.";
1425
1453
  }
1426
- return "Erro inesperado ao falar com o Nexus Flow.";
1454
+ return "Erro inesperado ao falar com o H1VE.";
1427
1455
  }
1428
1456
  main(process.argv.slice(2)).then((code) => {
1429
1457
  process.exitCode = code;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@h1veframework/cli",
3
- "version": "0.6.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": [