forgehive 0.7.5 → 0.7.7

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 CHANGED
@@ -14,8 +14,8 @@
14
14
  <p align="center">
15
15
  <img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node.js ≥ 18">
16
16
  <img src="https://img.shields.io/badge/typescript-5.8-blue" alt="TypeScript">
17
- <img src="https://img.shields.io/badge/tests-267%20passing-success" alt="267 tests">
18
- <img src="https://img.shields.io/badge/bundle-247KB-lightgrey" alt="247KB bundle">
17
+ <img src="https://img.shields.io/badge/tests-273%20passing-success" alt="273 tests">
18
+ <img src="https://img.shields.io/badge/bundle-259KB-lightgrey" alt="259KB bundle">
19
19
  <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT">
20
20
  </p>
21
21
 
@@ -45,9 +45,9 @@ Claude loses all project knowledge between sessions. forgehive solves this by wr
45
45
 
46
46
  ---
47
47
 
48
- ## Status: v0.7 — Stable
48
+ ## Status: v0.7.5 — Stable
49
49
 
50
- 267 passing tests back the commands listed below. The v0.7 feature set has been validated end-to-end.
50
+ 273 passing tests back the commands listed below. The v0.7 feature set has been validated end-to-end.
51
51
 
52
52
  **Stable — use with confidence:**
53
53
 
@@ -93,6 +93,15 @@ If you run into issues, please open an issue on GitHub. The most valuable feedba
93
93
 
94
94
  ---
95
95
 
96
+ ## Documentation
97
+
98
+ | Document | Description |
99
+ |---|---|
100
+ | [User Guide](docs/user-guide.md) | Installation, quick start, all commands with examples, troubleshooting |
101
+ | [TUTORIAL.md](TUTORIAL.md) | Step-by-step walkthrough of a real session |
102
+
103
+ ---
104
+
96
105
  ## Installation
97
106
 
98
107
  ### From npm (recommended)
@@ -109,14 +118,14 @@ This makes both `fh` and `forgehive` available globally.
109
118
  git clone https://github.com/matharnica/forgehive
110
119
  cd forgehive
111
120
  npm install
112
- npm run build # compiles to dist/cli.js (~247 KB)
121
+ npm run build # compiles to dist/cli.js (~259 KB)
113
122
  npm link # makes 'fh' available globally
114
123
  ```
115
124
 
116
125
  Verify the installation:
117
126
 
118
127
  ```bash
119
- fh --version # should print 0.7.2
128
+ fh --version # should print 0.7.5
120
129
  fh --help # lists all available commands
121
130
  ```
122
131
 
@@ -332,6 +341,23 @@ Parses conventional commits (`feat:`, `fix:`, `chore:`, `docs:`, etc.) and group
332
341
 
333
342
  ---
334
343
 
344
+ ### Docs
345
+
346
+ ```bash
347
+ fh docs # list existing documentation
348
+ fh docs user [--output path] # generate user guide → docs/user-guide.md
349
+ fh docs api [--output path] # generate API reference → docs/api.md
350
+ fh docs onboard [--output path] # generate onboarding doc → ONBOARDING.md
351
+ fh docs changelog [--since tag] [--output path] # generate changelog → CHANGELOG.md
352
+ fh docs adr "<title>" # create Architecture Decision Record
353
+ ```
354
+
355
+ Generates documentation from your project's stack, memory, and git history. Each subcommand writes to a default path inside `docs/` (created if missing) or to the path given via `--output`. `fh docs user` and `fh docs api` write to `docs/user-guide.md` and `docs/api.md` respectively. `fh docs adr` creates a numbered ADR file in `.forgehive/memory/adrs/`.
356
+
357
+ > `fh docs onboard` and `fh onboard` both generate onboarding documentation — they share the same generator. `fh docs onboard` allows specifying a custom `--output` path; `fh onboard` writes to stdout by default.
358
+
359
+ ---
360
+
335
361
  ### Developer Metrics
336
362
 
337
363
  ```bash
@@ -847,9 +873,9 @@ Global credential store (chmod 600). Managed exclusively via `fh mcp auth` comma
847
873
  |---|---|
848
874
  | Runtime | Node.js ≥ 18, ESM |
849
875
  | Language | TypeScript |
850
- | Build | esbuild -> `dist/cli.js` (~247 KB, single bundle) |
876
+ | Build | esbuild -> `dist/cli.js` (~259 KB, single bundle) |
851
877
  | Type check | `tsc --noEmit` |
852
- | Tests | `node:test` (native) + tsx ESM loader, 267 tests |
878
+ | Tests | `node:test` (native) + tsx ESM loader, 273 tests |
853
879
  | Dependencies | `js-yaml` (sole runtime dependency) |
854
880
 
855
881
  ```bash
@@ -860,6 +886,19 @@ npm test # node --import tsx/esm --test test/*.test.ts
860
886
 
861
887
  ---
862
888
 
889
+ ## Changelog
890
+
891
+ | Version | What's new |
892
+ |---|---|
893
+ | **0.7.5** | `fh --version` reads from `package.json` (no longer hardcoded) |
894
+ | **0.7.4** | Party slash commands installed by `fh init` (`/party`, `/review-party`, `/design-party`, `/full-party`, `/security-party`) |
895
+ | **0.7.3** | User Docs generation (`fh docs user`, `fh docs api`, `fh docs list`) |
896
+ | **0.7.2** | `fh --help`, `fh init --force`, `fh story sprint`, `fh velocity show` fixes |
897
+ | **0.7.1** | Story Cards, Epics, Velocity tracking |
898
+ | **0.7.0** | CI, Map, Onboard, Changelog, Metrics, Sync, Background agents |
899
+
900
+ ---
901
+
863
902
  ## License
864
903
 
865
904
  MIT
package/dist/cli.js CHANGED
@@ -6460,7 +6460,7 @@ import fs30 from "node:fs";
6460
6460
  import path31 from "node:path";
6461
6461
  import { spawnSync as spawnSync11 } from "node:child_process";
6462
6462
  var SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".py", ".go"];
6463
- var IGNORE_DIRS3 = ["node_modules", ".git", "dist", ".forgehive", "coverage", ".next", "build", "test"];
6463
+ var IGNORE_DIRS3 = ["node_modules", ".git", "dist", ".forgehive", "coverage", ".next", "build", "test", "__tests__", "spec"];
6464
6464
  var EXPORT_PATTERNS = [
6465
6465
  /^export\s+(?:async\s+)?function\s+(\w+)/gm,
6466
6466
  /^export\s+(?:const|let|var)\s+(\w+)/gm,
@@ -6476,6 +6476,27 @@ function readCapabilities2(forgehiveDir2) {
6476
6476
  return {};
6477
6477
  }
6478
6478
  }
6479
+ function extractCapabilityInfo(caps) {
6480
+ if (typeof caps.language === "string") {
6481
+ return {
6482
+ language: caps.language,
6483
+ packageManager: typeof caps.packageManager === "string" ? caps.packageManager : void 0,
6484
+ entryPoints: Array.isArray(caps.entryPoints) ? caps.entryPoints : void 0
6485
+ };
6486
+ }
6487
+ const confirmed = caps.capabilities?.confirmed ?? [];
6488
+ const ids = confirmed.map((c) => c.id ?? "").filter(Boolean);
6489
+ let language;
6490
+ if (ids.includes("typescript")) language = "typescript";
6491
+ else if (ids.includes("javascript")) language = "javascript";
6492
+ else if (ids.includes("python")) language = "python";
6493
+ else if (ids.includes("go")) language = "go";
6494
+ let packageManager;
6495
+ if (ids.includes("pnpm")) packageManager = "pnpm";
6496
+ else if (ids.includes("yarn")) packageManager = "yarn";
6497
+ else if (ids.includes("npm")) packageManager = "npm";
6498
+ return { language, packageManager };
6499
+ }
6479
6500
  function readMemoryFiles2(forgehiveDir2) {
6480
6501
  const memDir = path31.join(forgehiveDir2, "memory");
6481
6502
  if (!fs30.existsSync(memDir)) return {};
@@ -6518,6 +6539,7 @@ function walkSourceFiles(dir) {
6518
6539
  function generateUserGuide(projectRoot2, forgehiveDir2) {
6519
6540
  const projectName = path31.basename(projectRoot2);
6520
6541
  const caps = readCapabilities2(forgehiveDir2);
6542
+ const { language: lang, packageManager, entryPoints } = extractCapabilityInfo(caps);
6521
6543
  const memFiles = readMemoryFiles2(forgehiveDir2);
6522
6544
  const commits = getRecentCommits2(projectRoot2);
6523
6545
  const lines = [];
@@ -6528,16 +6550,15 @@ function generateUserGuide(projectRoot2, forgehiveDir2) {
6528
6550
  lines.push("## Overview");
6529
6551
  lines.push("");
6530
6552
  if (memFiles["project.md"]) {
6531
- const content = memFiles["project.md"].replace(/^---[\s\S]*?---\n/m, "").trim();
6553
+ const content = memFiles["project.md"].replace(/^---[\s\S]*?---\n/, "").trim();
6532
6554
  lines.push(content);
6533
6555
  } else {
6534
- lines.push(`${projectName} is a ${caps.language ?? "software"} application.`);
6556
+ lines.push(`${projectName} is a ${lang ?? "software"} application.`);
6535
6557
  }
6536
6558
  lines.push("");
6537
6559
  lines.push("## Requirements");
6538
6560
  lines.push("");
6539
- const pm = caps.packageManager ?? "npm";
6540
- const lang = caps.language;
6561
+ const pm = packageManager ?? "npm";
6541
6562
  if (lang === "typescript" || lang === "javascript") {
6542
6563
  lines.push("- **Node.js** \u2265 18");
6543
6564
  lines.push(`- **${pm}** (package manager)`);
@@ -6564,7 +6585,6 @@ function generateUserGuide(projectRoot2, forgehiveDir2) {
6564
6585
  }
6565
6586
  lines.push("```");
6566
6587
  lines.push("");
6567
- const entryPoints = caps.entryPoints;
6568
6588
  if (Array.isArray(entryPoints) && entryPoints.length > 0) {
6569
6589
  lines.push("## Getting Started");
6570
6590
  lines.push("");
@@ -6576,7 +6596,7 @@ function generateUserGuide(projectRoot2, forgehiveDir2) {
6576
6596
  if (memFiles["stack.md"]) {
6577
6597
  lines.push("## Configuration");
6578
6598
  lines.push("");
6579
- const content = memFiles["stack.md"].replace(/^---[\s\S]*?---\n/m, "").trim();
6599
+ const content = memFiles["stack.md"].replace(/^---[\s\S]*?---\n/, "").trim();
6580
6600
  lines.push(content);
6581
6601
  lines.push("");
6582
6602
  }
@@ -7665,8 +7685,8 @@ Setze diese Umgebungsvariablen:`);
7665
7685
  pkg = JSON.parse(fs31.readFileSync(path32.join(projectRoot, "package.json"), "utf8"));
7666
7686
  } catch {
7667
7687
  }
7668
- const version2 = pkg.version ?? "unreleased";
7669
- const md = formatChangelog(commits, version2);
7688
+ const pkgVersion = pkg.version ?? "unreleased";
7689
+ const md = formatChangelog(commits, pkgVersion);
7670
7690
  const outputPath = outputArg ?? path32.join(projectRoot, "CHANGELOG.md");
7671
7691
  let existing = "";
7672
7692
  if (fs31.existsSync(outputPath)) existing = fs31.readFileSync(outputPath, "utf8");
@@ -7680,7 +7700,7 @@ Setze diese Umgebungsvariablen:`);
7680
7700
  }
7681
7701
  const adrsDir = path32.join(forgehiveDir, "memory", "adrs");
7682
7702
  fs31.mkdirSync(adrsDir, { recursive: true });
7683
- const existing = fs31.existsSync(adrsDir) ? fs31.readdirSync(adrsDir).filter((f) => f.endsWith(".md")).length : 0;
7703
+ const existing = fs31.readdirSync(adrsDir).filter((f) => f.endsWith(".md")).length;
7684
7704
  const adrId = String(existing + 1).padStart(4, "0");
7685
7705
  const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
7686
7706
  const filename = `${adrId}-${slug}.md`;