@h1veframework/cli 0.12.2 → 0.14.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 (2) hide show
  1. package/dist/index.js +90 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -61,6 +61,21 @@ async function switchBranch(name, runner = defaultGitRunner) {
61
61
  throw new Error("GIT_SWITCH_FAILED");
62
62
  }
63
63
  }
64
+ async function commitsBehindOriginMain(runner = defaultGitRunner) {
65
+ try {
66
+ await runner(["fetch", "origin"]);
67
+ } catch {
68
+ return null;
69
+ }
70
+ let out;
71
+ try {
72
+ out = await runner(["rev-list", "--count", "HEAD..origin/main"]);
73
+ } catch {
74
+ return null;
75
+ }
76
+ const n = Number(out.trim());
77
+ return Number.isInteger(n) && n >= 0 ? n : null;
78
+ }
64
79
  async function listLocalBranches(runner = defaultGitRunner) {
65
80
  let out;
66
81
  try {
@@ -197,6 +212,9 @@ function createClient(config) {
197
212
  setDone(featureId, doneContent) {
198
213
  return writeAction("PATCH", featureId, "done", { done_content: doneContent });
199
214
  },
215
+ addDependency(featureId, dependsOnId) {
216
+ return postAction(featureId, "dependencies", { depends_on_id: dependsOnId });
217
+ },
200
218
  // SPEC-100 (B2): best-effort — o juiz de drift é ADVISORY; qualquer falha → {checked:false}
201
219
  // (nunca lança, pra não estragar o fluxo de gravar a SPEC no `set_spec` do MCP).
202
220
  async checkSpecAlignment(featureId) {
@@ -360,6 +378,26 @@ async function writeEnvVars(pairs, opts = {}) {
360
378
  return { file, keys: Object.keys(pairs) };
361
379
  }
362
380
 
381
+ // ../core/src/format.ts
382
+ var MINUTE_MS = 6e4;
383
+ var HOUR_MS = 36e5;
384
+ var DAY_MS = 864e5;
385
+ function formatDuration(ms) {
386
+ if (ms < MINUTE_MS) return "<1min";
387
+ if (ms < HOUR_MS) return `${Math.floor(ms / MINUTE_MS)}min`;
388
+ if (ms < DAY_MS) {
389
+ const h2 = Math.floor(ms / HOUR_MS);
390
+ const m = Math.floor(ms % HOUR_MS / MINUTE_MS);
391
+ return m > 0 ? `${h2}h ${m}min` : `${h2}h`;
392
+ }
393
+ const d = Math.floor(ms / DAY_MS);
394
+ const h = Math.floor(ms % DAY_MS / HOUR_MS);
395
+ return h > 0 ? `${d}d ${h}h` : `${d}d`;
396
+ }
397
+ function formatMinutes(minutes) {
398
+ return formatDuration(minutes * MINUTE_MS);
399
+ }
400
+
363
401
  // ../core/src/credentials.ts
364
402
  import {
365
403
  readFile as fsReadFile2,
@@ -457,6 +495,9 @@ async function clientFromConfig(cfg) {
457
495
  }
458
496
 
459
497
  // src/format.ts
498
+ function activeTime(d) {
499
+ return d.elapsed_minutes != null ? formatMinutes(d.elapsed_minutes) : `${d.days_active}d`;
500
+ }
460
501
  var STAGE_LABEL = {
461
502
  backlog: "Backlog",
462
503
  spec: "Spec",
@@ -476,7 +517,7 @@ function formatStatus(d) {
476
517
  const lines = [
477
518
  `${f.name} [${f.priority}]`,
478
519
  `Stage: ${stageLabel(d.stage)}`,
479
- `Dias ativos: ${d.days_active}`,
520
+ `Tempo ativo: ${activeTime(d)}`,
480
521
  `Branch: ${f.branch_slug ?? "\u2014"}`
481
522
  ];
482
523
  if (f.github_pr_url) lines.push(`PR: ${f.github_pr_url}`);
@@ -516,10 +557,30 @@ function formatStartList(list) {
516
557
  const header = "Features para iniciar ou retomar \u2014 escolha: h1ve start <n\xBA>";
517
558
  const rows = list.map((f, i) => {
518
559
  const tag = f.branch_slug !== null ? " (retomar)" : "";
519
- return ` ${String(i + 1).padStart(2)}. ${f.name} [${stageLabel(f.stage)}]${tag}`;
560
+ const dep = f.depends_on && f.depends_on.length > 0 ? ` \u26A0 depende de ${f.depends_on.map((d) => d.name).join(", ")}` : "";
561
+ return ` ${String(i + 1).padStart(2)}. ${f.name} [${stageLabel(f.stage)}]${tag}${dep}`;
520
562
  });
521
563
  return [header, ...rows].join("\n");
522
564
  }
565
+ var PRIORITY_RANK = { HIGH: 0, MEDIUM: 1, LOW: 2 };
566
+ function rankStartable(list) {
567
+ return [...list].sort((a, b) => {
568
+ const resume = (a.branch_slug !== null ? 0 : 1) - (b.branch_slug !== null ? 0 : 1);
569
+ if (resume !== 0) return resume;
570
+ const blocked = (a.blocked_by_dep ? 1 : 0) - (b.blocked_by_dep ? 1 : 0);
571
+ if (blocked !== 0) return blocked;
572
+ const prio = PRIORITY_RANK[a.priority] - PRIORITY_RANK[b.priority];
573
+ if (prio !== 0) return prio;
574
+ return (a.appetite ?? 0) - (b.appetite ?? 0);
575
+ });
576
+ }
577
+ function formatDepWarning(f) {
578
+ if (!f.depends_on || f.depends_on.length === 0) return "";
579
+ const names = f.depends_on.map((d) => d.name).join(", ");
580
+ return `\u26A0 "${f.name}" depende de ${names} (ainda n\xE3o conclu\xEDdo) \u2014 considere fazer isso primeiro.
581
+
582
+ `;
583
+ }
523
584
  var PRE_DEV_STAGES = /* @__PURE__ */ new Set(["backlog", "spec", "blocked"]);
524
585
  function startFlowLines(r) {
525
586
  const stage = r.landed_stage ?? r.feature.stage;
@@ -554,6 +615,13 @@ function formatStart(r, opts) {
554
615
  ...startFlowLines(r)
555
616
  ].join("\n");
556
617
  }
618
+ function formatBehindMainWarning(behind) {
619
+ const plural = behind === 1 ? "commit" : "commits";
620
+ return [
621
+ `\u26A0 Sua base est\xE1 ${behind} ${plural} atr\xE1s de origin/main.`,
622
+ " Sincronize antes de codar: git fetch origin && git rebase origin/main"
623
+ ].join("\n");
624
+ }
557
625
  function formatHealth(snaps) {
558
626
  if (!snaps.length) return "Nenhum snapshot de sa\xFAde t\xE9cnica registrado.";
559
627
  const header = "Semana CI% Deploys Bugs D\xEDvida";
@@ -705,6 +773,17 @@ async function health(io) {
705
773
  }
706
774
 
707
775
  // src/commands/start.ts
776
+ async function behindMainWarning(io) {
777
+ if (!io.checkBehindMain) return "";
778
+ try {
779
+ const behind = await io.checkBehindMain();
780
+ return behind && behind > 0 ? `${formatBehindMainWarning(behind)}
781
+
782
+ ` : "";
783
+ } catch {
784
+ return "";
785
+ }
786
+ }
708
787
  function branchSlugOf(branch) {
709
788
  return branch.match(/^feat\/[^/]+\/(.+)$/)?.[1] ?? null;
710
789
  }
@@ -725,7 +804,7 @@ function pick(list, arg) {
725
804
  }
726
805
  async function start(io, args) {
727
806
  const [features, localBranches] = await Promise.all([io.client.listFeatures(), io.listBranches()]);
728
- const list = startable(features, localBranches);
807
+ const list = rankStartable(startable(features, localBranches));
729
808
  if (list.length === 0) {
730
809
  throw new CliError("Nenhuma feature para iniciar ou retomar (sem branch local e fora de main).");
731
810
  }
@@ -733,6 +812,7 @@ async function start(io, args) {
733
812
  if (!selected) {
734
813
  return { human: formatStartList(list), data: { features: list } };
735
814
  }
815
+ const warning = await behindMainWarning(io);
736
816
  const resuming = selected.branch_slug !== null;
737
817
  const result = await io.client.startFeature(selected.id, args.slug);
738
818
  try {
@@ -742,7 +822,8 @@ async function start(io, args) {
742
822
  if (code === "BRANCH_EXISTS") await io.switchBranch(result.branch_name);
743
823
  else throw err;
744
824
  }
745
- return { human: formatStart(result, { resumed: resuming }), data: result };
825
+ const depWarn = formatDepWarning(selected);
826
+ return { human: `${warning}${depWarn}${formatStart(result, { resumed: resuming })}`, data: result };
746
827
  }
747
828
 
748
829
  // src/commands/connect.ts
@@ -1437,8 +1518,11 @@ async function mcp(io, args) {
1437
1518
  };
1438
1519
  }
1439
1520
 
1521
+ // src/version.ts
1522
+ import { readFileSync } from "fs";
1523
+ var VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
1524
+
1440
1525
  // src/index.ts
1441
- var VERSION = "0.12.2";
1442
1526
  var HELP = `h1ve \u2014 CLI do H1VE
1443
1527
  (o comando legado \`nf\` continua funcionando como alias)
1444
1528
 
@@ -1562,6 +1646,7 @@ ${HELP}`);
1562
1646
  createBranch: (name) => createBranch(name, defaultGitRunner),
1563
1647
  switchBranch: (name) => switchBranch(name, defaultGitRunner),
1564
1648
  listBranches: () => listLocalBranches(defaultGitRunner),
1649
+ checkBehindMain: () => commitsBehindOriginMain(defaultGitRunner),
1565
1650
  readFile: (path) => readFile(path, "utf8"),
1566
1651
  readStdin,
1567
1652
  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.12.2",
3
+ "version": "0.14.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",