forgehive 0.7.2 → 0.7.4

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
@@ -15,7 +15,7 @@
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
17
  <img src="https://img.shields.io/badge/tests-267%20passing-success" alt="267 tests">
18
- <img src="https://img.shields.io/badge/bundle-244KB-lightgrey" alt="244KB bundle">
18
+ <img src="https://img.shields.io/badge/bundle-247KB-lightgrey" alt="247KB bundle">
19
19
  <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT">
20
20
  </p>
21
21
 
@@ -109,14 +109,14 @@ This makes both `fh` and `forgehive` available globally.
109
109
  git clone https://github.com/matharnica/forgehive
110
110
  cd forgehive
111
111
  npm install
112
- npm run build # compiles to dist/cli.js (~244 KB)
112
+ npm run build # compiles to dist/cli.js (~247 KB)
113
113
  npm link # makes 'fh' available globally
114
114
  ```
115
115
 
116
116
  Verify the installation:
117
117
 
118
118
  ```bash
119
- fh --version # should print 0.7.1
119
+ fh --version # should print 0.7.2
120
120
  fh --help # lists all available commands
121
121
  ```
122
122
 
@@ -166,11 +166,15 @@ fh security report gdpr # generates a CISO-ready compliance report
166
166
 
167
167
  | Command | Description |
168
168
  |---|---|
169
- | `fh init` | Scan project, create `.forgehive/`, merge CLAUDE.md block, write AGENTS.md |
169
+ | `fh --help` | Show full command reference |
170
+ | `fh -h` | Same as --help |
171
+ | `fh init` | Set up forgehive in the current project. Use --force to re-initialize an existing setup |
170
172
  | `fh confirm` | Confirm `capabilities.yaml` — activates context for Claude |
171
173
  | `fh rollback` | Remove `.forgehive/` and the CLAUDE.md block cleanly |
172
174
  | `fh status` | Show current project status, drift warning if scan is outdated |
173
175
 
176
+ > Running `fh init` on a project that already has `.forgehive/` prints a warning and exits. Use `fh init --force` to re-initialize (overwrites `capabilities.yaml` and scans again). To update only the scan, use `fh scan --update`.
177
+
174
178
  **When to use `fh rollback`:** If you want to remove forgehive from a project entirely. It removes exactly the block it inserted into CLAUDE.md (nothing else) and deletes `.forgehive/`.
175
179
 
176
180
  ---
@@ -407,11 +411,12 @@ fh story create "As a user I want to log in" --epic EPC-1 --points 3 # with ep
407
411
  fh story list # list all backlog stories
408
412
  fh story list --epic EPC-1 # filter by epic
409
413
  fh story show US-1 # show full story card with acceptance criteria
414
+ fh story sprint US-1 # mark story as in-sprint (pulled into current sprint)
410
415
  fh story done US-1 # mark story as done
411
416
  fh story done US-1 --points 5 # mark done and record actual points
412
417
  ```
413
418
 
414
- Each story card is a Markdown file at `.forgehive/memory/stories/US-N.md`. The card includes a title, acceptance criteria placeholder, story points, epic link, and status (`backlog` | `done`). When you run `/fh-sprint` in a Claude Code session, Claude reads the backlog and uses velocity data to suggest a realistic sprint scope.
419
+ Each story card is a Markdown file at `.forgehive/memory/stories/US-N.md`. The card includes a title, acceptance criteria placeholder, story points, epic link, and status (`backlog` | `in-sprint` | `done`). Stories move through `backlog → in-sprint → done`. When you run `/fh-sprint` in a Claude Code session, Claude reads the backlog and uses velocity data to suggest a realistic sprint scope.
415
420
 
416
421
  ---
417
422
 
@@ -842,7 +847,7 @@ Global credential store (chmod 600). Managed exclusively via `fh mcp auth` comma
842
847
  |---|---|
843
848
  | Runtime | Node.js ≥ 18, ESM |
844
849
  | Language | TypeScript |
845
- | Build | esbuild -> `dist/cli.js` (~244 KB, single bundle) |
850
+ | Build | esbuild -> `dist/cli.js` (~247 KB, single bundle) |
846
851
  | Type check | `tsc --noEmit` |
847
852
  | Tests | `node:test` (native) + tsx ESM loader, 267 tests |
848
853
  | Dependencies | `js-yaml` (sole runtime dependency) |
package/dist/cli.js CHANGED
@@ -2751,8 +2751,8 @@ var init_harness = __esm({
2751
2751
 
2752
2752
  // src/cli.ts
2753
2753
  init_js_yaml();
2754
- import fs30 from "node:fs";
2755
- import path31 from "node:path";
2754
+ import fs31 from "node:fs";
2755
+ import path32 from "node:path";
2756
2756
 
2757
2757
  // src/scanner.ts
2758
2758
  import fs from "node:fs";
@@ -6454,17 +6454,206 @@ function formatVelocityReport(history) {
6454
6454
  return lines.join("\n");
6455
6455
  }
6456
6456
 
6457
+ // src/docs.ts
6458
+ init_js_yaml();
6459
+ import fs30 from "node:fs";
6460
+ import path31 from "node:path";
6461
+ import { spawnSync as spawnSync11 } from "node:child_process";
6462
+ var SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".py", ".go"];
6463
+ var IGNORE_DIRS3 = ["node_modules", ".git", "dist", ".forgehive", "coverage", ".next", "build", "test"];
6464
+ var EXPORT_PATTERNS = [
6465
+ /^export\s+(?:async\s+)?function\s+(\w+)/gm,
6466
+ /^export\s+(?:const|let|var)\s+(\w+)/gm,
6467
+ /^export\s+(?:class|interface|type|enum)\s+(\w+)/gm,
6468
+ /^export\s+default\s+(?:function\s+)?(\w+)?/gm
6469
+ ];
6470
+ function readCapabilities2(forgehiveDir2) {
6471
+ const capPath = path31.join(forgehiveDir2, "capabilities.yaml");
6472
+ if (!fs30.existsSync(capPath)) return {};
6473
+ try {
6474
+ return jsYaml.load(fs30.readFileSync(capPath, "utf8")) ?? {};
6475
+ } catch {
6476
+ return {};
6477
+ }
6478
+ }
6479
+ function readMemoryFiles2(forgehiveDir2) {
6480
+ const memDir = path31.join(forgehiveDir2, "memory");
6481
+ if (!fs30.existsSync(memDir)) return {};
6482
+ const result = {};
6483
+ for (const f of fs30.readdirSync(memDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md")) {
6484
+ result[f] = fs30.readFileSync(path31.join(memDir, f), "utf8");
6485
+ }
6486
+ return result;
6487
+ }
6488
+ function getRecentCommits2(projectRoot2, n = 10) {
6489
+ const result = spawnSync11("git", ["log", "--oneline", `-${n}`], { cwd: projectRoot2, encoding: "utf8" });
6490
+ if (result.status !== 0) return [];
6491
+ return result.stdout.trim().split("\n").filter(Boolean);
6492
+ }
6493
+ function extractExports(content) {
6494
+ const exports = [];
6495
+ for (const pattern of EXPORT_PATTERNS) {
6496
+ pattern.lastIndex = 0;
6497
+ let m;
6498
+ while ((m = pattern.exec(content)) !== null) {
6499
+ if (m[1]) exports.push(m[1]);
6500
+ }
6501
+ }
6502
+ return [...new Set(exports)];
6503
+ }
6504
+ function walkSourceFiles(dir) {
6505
+ const results = [];
6506
+ function walk(current) {
6507
+ if (!fs30.existsSync(current)) return;
6508
+ for (const entry of fs30.readdirSync(current, { withFileTypes: true })) {
6509
+ if (IGNORE_DIRS3.includes(entry.name)) continue;
6510
+ const full = path31.join(current, entry.name);
6511
+ if (entry.isDirectory()) walk(full);
6512
+ else if (entry.isFile() && SOURCE_EXTS.includes(path31.extname(entry.name))) results.push(full);
6513
+ }
6514
+ }
6515
+ walk(dir);
6516
+ return results;
6517
+ }
6518
+ function generateUserGuide(projectRoot2, forgehiveDir2) {
6519
+ const projectName = path31.basename(projectRoot2);
6520
+ const caps = readCapabilities2(forgehiveDir2);
6521
+ const memFiles = readMemoryFiles2(forgehiveDir2);
6522
+ const commits = getRecentCommits2(projectRoot2);
6523
+ const lines = [];
6524
+ lines.push(`# ${projectName} \u2014 User Guide`);
6525
+ lines.push("");
6526
+ lines.push(`> Generated by forgehive on ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
6527
+ lines.push("");
6528
+ lines.push("## Overview");
6529
+ lines.push("");
6530
+ if (memFiles["project.md"]) {
6531
+ const content = memFiles["project.md"].replace(/^---[\s\S]*?---\n/m, "").trim();
6532
+ lines.push(content);
6533
+ } else {
6534
+ lines.push(`${projectName} is a ${caps.language ?? "software"} application.`);
6535
+ }
6536
+ lines.push("");
6537
+ lines.push("## Requirements");
6538
+ lines.push("");
6539
+ const pm = caps.packageManager ?? "npm";
6540
+ const lang = caps.language;
6541
+ if (lang === "typescript" || lang === "javascript") {
6542
+ lines.push("- **Node.js** \u2265 18");
6543
+ lines.push(`- **${pm}** (package manager)`);
6544
+ } else if (lang === "python") {
6545
+ lines.push("- **Python** \u2265 3.9");
6546
+ lines.push("- **pip** or **poetry**");
6547
+ } else if (lang === "go") {
6548
+ lines.push("- **Go** \u2265 1.21");
6549
+ }
6550
+ lines.push("");
6551
+ lines.push("## Installation");
6552
+ lines.push("");
6553
+ lines.push("```bash");
6554
+ lines.push("# Clone the repository");
6555
+ lines.push(`git clone <repo-url>`);
6556
+ lines.push(`cd ${projectName}`);
6557
+ lines.push("");
6558
+ if (lang === "typescript" || lang === "javascript") {
6559
+ lines.push(`${pm === "yarn" ? "yarn" : pm === "pnpm" ? "pnpm install" : "npm install"}`);
6560
+ } else if (lang === "python") {
6561
+ lines.push("pip install -r requirements.txt");
6562
+ } else if (lang === "go") {
6563
+ lines.push("go mod download");
6564
+ }
6565
+ lines.push("```");
6566
+ lines.push("");
6567
+ const entryPoints = caps.entryPoints;
6568
+ if (Array.isArray(entryPoints) && entryPoints.length > 0) {
6569
+ lines.push("## Getting Started");
6570
+ lines.push("");
6571
+ lines.push("```bash");
6572
+ for (const ep of entryPoints.slice(0, 3)) lines.push(`node ${ep}`);
6573
+ lines.push("```");
6574
+ lines.push("");
6575
+ }
6576
+ if (memFiles["stack.md"]) {
6577
+ lines.push("## Configuration");
6578
+ lines.push("");
6579
+ const content = memFiles["stack.md"].replace(/^---[\s\S]*?---\n/m, "").trim();
6580
+ lines.push(content);
6581
+ lines.push("");
6582
+ }
6583
+ if (commits.length > 0) {
6584
+ lines.push("## Changelog");
6585
+ lines.push("");
6586
+ lines.push("Recent changes:");
6587
+ lines.push("");
6588
+ for (const c of commits.slice(0, 5)) lines.push(`- ${c}`);
6589
+ lines.push("");
6590
+ lines.push("Run `fh changelog` for the full changelog.");
6591
+ lines.push("");
6592
+ }
6593
+ lines.push("## Support");
6594
+ lines.push("");
6595
+ lines.push("For issues, open a ticket in the project repository.");
6596
+ return lines.join("\n");
6597
+ }
6598
+ function generateApiReference(projectRoot2) {
6599
+ const srcDir = path31.join(projectRoot2, "src");
6600
+ const searchDir = fs30.existsSync(srcDir) ? srcDir : projectRoot2;
6601
+ const files = walkSourceFiles(searchDir);
6602
+ const lines = [];
6603
+ lines.push("# API Reference");
6604
+ lines.push("");
6605
+ lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
6606
+ lines.push("");
6607
+ if (files.length === 0) {
6608
+ lines.push("No source files found.");
6609
+ return lines.join("\n");
6610
+ }
6611
+ for (const filePath of files) {
6612
+ let content = "";
6613
+ try {
6614
+ content = fs30.readFileSync(filePath, "utf8");
6615
+ } catch {
6616
+ continue;
6617
+ }
6618
+ const exports = extractExports(content);
6619
+ if (exports.length === 0) continue;
6620
+ const relPath = path31.relative(projectRoot2, filePath);
6621
+ lines.push(`## \`${relPath}\``);
6622
+ lines.push("");
6623
+ lines.push("**Exports:**");
6624
+ lines.push("");
6625
+ for (const exp of exports) lines.push(`- \`${exp}\``);
6626
+ lines.push("");
6627
+ }
6628
+ return lines.join("\n");
6629
+ }
6630
+ function listExistingDocs(projectRoot2) {
6631
+ const docs = [];
6632
+ const docsDir = path31.join(projectRoot2, "docs");
6633
+ if (fs30.existsSync(docsDir)) {
6634
+ for (const f of fs30.readdirSync(docsDir)) {
6635
+ if (f.endsWith(".md")) docs.push(path31.join(docsDir, f));
6636
+ }
6637
+ }
6638
+ const rootDocs = ["README.md", "CHANGELOG.md", "ONBOARDING.md", "CONTRIBUTING.md"];
6639
+ for (const f of rootDocs) {
6640
+ const full = path31.join(projectRoot2, f);
6641
+ if (fs30.existsSync(full)) docs.push(full);
6642
+ }
6643
+ return docs;
6644
+ }
6645
+
6457
6646
  // src/cli.ts
6458
6647
  var [, , command, subcommand, ...rest] = process.argv;
6459
6648
  var projectRoot = process.cwd();
6460
- var forgehiveDir = path31.join(projectRoot, ".forgehive");
6649
+ var forgehiveDir = path32.join(projectRoot, ".forgehive");
6461
6650
  if (command === "--version" || command === "-v") {
6462
- console.log("0.7.2");
6651
+ console.log("0.7.3");
6463
6652
  process.exit(0);
6464
6653
  }
6465
6654
  if (command === "--help" || command === "-h" || command === "help") {
6466
6655
  console.log(`
6467
- forgehive v0.7.2 \u2014 Context-aware AI development environment
6656
+ forgehive v0.7.3 \u2014 Context-aware AI development environment
6468
6657
 
6469
6658
  USAGE
6470
6659
  fh <command> [subcommand] [options]
@@ -6495,6 +6684,12 @@ CODEBASE
6495
6684
  fh onboard [--output path] Generate ONBOARDING.md
6496
6685
  fh changelog [--since tag] Semantic changelog from git
6497
6686
  fh metrics [--since date] Developer productivity metrics
6687
+ fh docs List existing documentation
6688
+ fh docs user [--output path] Generate user-facing guide (docs/user-guide.md)
6689
+ fh docs api [--output path] Generate API reference from exports (docs/api.md)
6690
+ fh docs onboard Generate onboarding doc
6691
+ fh docs changelog [--since tag] Generate changelog
6692
+ fh docs adr "<title>" Create Architecture Decision Record
6498
6693
 
6499
6694
  SPRINT PLANNING
6500
6695
  fh story create <title> [--epic EPC-N] [--points N]
@@ -6531,18 +6726,18 @@ COST
6531
6726
  process.exit(0);
6532
6727
  }
6533
6728
  function loadClaudeMdBlock() {
6534
- const templatePath = path31.join(
6535
- path31.dirname(new URL(import.meta.url).pathname),
6729
+ const templatePath = path32.join(
6730
+ path32.dirname(new URL(import.meta.url).pathname),
6536
6731
  "..",
6537
6732
  "forgehive",
6538
6733
  "templates",
6539
6734
  "claude-md.block.md"
6540
6735
  );
6541
- if (!fs30.existsSync(templatePath)) return "## forgehive\n\nSee .forgehive/ for configuration.";
6542
- return fs30.readFileSync(templatePath, "utf8");
6736
+ if (!fs31.existsSync(templatePath)) return "## forgehive\n\nSee .forgehive/ for configuration.";
6737
+ return fs31.readFileSync(templatePath, "utf8");
6543
6738
  }
6544
6739
  if (command === "init") {
6545
- const forgehiveDirExists = fs30.existsSync(forgehiveDir);
6740
+ const forgehiveDirExists = fs31.existsSync(forgehiveDir);
6546
6741
  if (forgehiveDirExists && !rest.includes("--force")) {
6547
6742
  console.log(`\u26A0 .forgehive/ existiert bereits in diesem Projekt.`);
6548
6743
  console.log(` Nutze 'fh init --force' um neu zu initialisieren (\xFCberschreibt capabilities.yaml).`);
@@ -6560,9 +6755,9 @@ if (command === "init") {
6560
6755
  const block = loadClaudeMdBlock();
6561
6756
  writeForgehiveDir(projectRoot, scanResult, capMap, block);
6562
6757
  const hash = computeHash(projectRoot);
6563
- fs30.writeFileSync(path31.join(forgehiveDir, ".scan-hash"), hash, "utf8");
6564
- const runtimeDir = path31.join(
6565
- path31.dirname(new URL(import.meta.url).pathname),
6758
+ fs31.writeFileSync(path32.join(forgehiveDir, ".scan-hash"), hash, "utf8");
6759
+ const runtimeDir = path32.join(
6760
+ path32.dirname(new URL(import.meta.url).pathname),
6566
6761
  "..",
6567
6762
  "forgehive"
6568
6763
  );
@@ -6594,7 +6789,7 @@ if (command === "init") {
6594
6789
  process.exit(1);
6595
6790
  }
6596
6791
  } else if (command === "memory") {
6597
- if (!fs30.existsSync(forgehiveDir)) {
6792
+ if (!fs31.existsSync(forgehiveDir)) {
6598
6793
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6599
6794
  process.exit(1);
6600
6795
  }
@@ -6603,7 +6798,7 @@ if (command === "init") {
6603
6798
  } else if (subcommand === "clean") {
6604
6799
  cleanMemory(forgehiveDir);
6605
6800
  } else if (subcommand === "export") {
6606
- const outputPath = rest[0] ?? path31.join(projectRoot, "forgehive-memory-export.md");
6801
+ const outputPath = rest[0] ?? path32.join(projectRoot, "forgehive-memory-export.md");
6607
6802
  try {
6608
6803
  exportMemory(forgehiveDir, outputPath);
6609
6804
  } catch (err) {
@@ -6651,7 +6846,7 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6651
6846
  } else if (subcommand === "snapshot") {
6652
6847
  const snapAction = rest[0];
6653
6848
  if (snapAction === "export") {
6654
- const outPath = rest[1] ?? path31.join(
6849
+ const outPath = rest[1] ?? path32.join(
6655
6850
  projectRoot,
6656
6851
  `forgehive-snapshot-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.json`
6657
6852
  );
@@ -6691,11 +6886,11 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6691
6886
  process.exit(1);
6692
6887
  }
6693
6888
  } else if (command === "scan" && subcommand === "--update") {
6694
- if (!fs30.existsSync(forgehiveDir)) {
6889
+ if (!fs31.existsSync(forgehiveDir)) {
6695
6890
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6696
6891
  process.exit(1);
6697
6892
  }
6698
- const savedHash = fs30.existsSync(path31.join(forgehiveDir, ".scan-hash")) ? fs30.readFileSync(path31.join(forgehiveDir, ".scan-hash"), "utf8").trim() : null;
6893
+ const savedHash = fs31.existsSync(path32.join(forgehiveDir, ".scan-hash")) ? fs31.readFileSync(path32.join(forgehiveDir, ".scan-hash"), "utf8").trim() : null;
6699
6894
  const currentHash = computeHash(projectRoot);
6700
6895
  if (savedHash === currentHash) {
6701
6896
  console.log("\u2713 Keine \xC4nderungen erkannt \u2014 capabilities.yaml ist aktuell");
@@ -6703,7 +6898,7 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6703
6898
  }
6704
6899
  console.log("\u{1F50D} \xC4nderungen erkannt \u2014 scanne erneut...\n");
6705
6900
  const oldDoc = jsYaml.load(
6706
- fs30.readFileSync(path31.join(forgehiveDir, "capabilities.yaml"), "utf8")
6901
+ fs31.readFileSync(path32.join(forgehiveDir, "capabilities.yaml"), "utf8")
6707
6902
  );
6708
6903
  const oldMap = { confirmed: oldDoc.capabilities.confirmed ?? [], inferred: [] };
6709
6904
  const scanResult = scan(projectRoot);
@@ -6723,16 +6918,16 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6723
6918
  console.log();
6724
6919
  const block = loadClaudeMdBlock();
6725
6920
  writeForgehiveDir(projectRoot, scanResult, newMap, block);
6726
- fs30.writeFileSync(path31.join(forgehiveDir, ".scan-hash"), currentHash, "utf8");
6921
+ fs31.writeFileSync(path32.join(forgehiveDir, ".scan-hash"), currentHash, "utf8");
6727
6922
  console.log("\u2713 scan-result.yaml und capabilities.yaml aktualisiert");
6728
6923
  console.log(" F\xFChre `fh confirm` aus um die \xC4nderungen zu best\xE4tigen");
6729
6924
  }
6730
6925
  } else if (command === "scan" && subcommand === "--check") {
6731
- if (!fs30.existsSync(path31.join(forgehiveDir, ".scan-hash"))) {
6926
+ if (!fs31.existsSync(path32.join(forgehiveDir, ".scan-hash"))) {
6732
6927
  console.log("Warnung: Kein Scan-Hash gefunden. F\xFChre `fh init` aus.");
6733
6928
  process.exit(1);
6734
6929
  }
6735
- const saved = fs30.readFileSync(path31.join(forgehiveDir, ".scan-hash"), "utf8").trim();
6930
+ const saved = fs31.readFileSync(path32.join(forgehiveDir, ".scan-hash"), "utf8").trim();
6736
6931
  const current = computeHash(projectRoot);
6737
6932
  if (saved !== current) {
6738
6933
  console.log("\u26A0 Codebase hat sich seit letztem Scan ge\xE4ndert.");
@@ -6741,7 +6936,7 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6741
6936
  }
6742
6937
  console.log("\u2713 capabilities.yaml ist aktuell");
6743
6938
  } else if (command === "skills") {
6744
- if (!fs30.existsSync(forgehiveDir)) {
6939
+ if (!fs31.existsSync(forgehiveDir)) {
6745
6940
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6746
6941
  process.exit(1);
6747
6942
  }
@@ -6777,7 +6972,7 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6777
6972
  process.exit(1);
6778
6973
  }
6779
6974
  } else if (command === "party") {
6780
- if (!fs30.existsSync(forgehiveDir)) {
6975
+ if (!fs31.existsSync(forgehiveDir)) {
6781
6976
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6782
6977
  process.exit(1);
6783
6978
  }
@@ -6898,7 +7093,7 @@ Ausf\xFChren mit --remove um zu l\xF6schen: fh memory prune ${days} --remove`);
6898
7093
  }
6899
7094
  }
6900
7095
  } else if (command === "wire") {
6901
- if (!fs30.existsSync(forgehiveDir)) {
7096
+ if (!fs31.existsSync(forgehiveDir)) {
6902
7097
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6903
7098
  process.exit(1);
6904
7099
  }
@@ -6937,7 +7132,7 @@ N\xE4chster Schritt: Setze die erforderlichen Umgebungsvariablen und starte Clau
6937
7132
  const limitIdx = allArgs.indexOf("--limit");
6938
7133
  const alertIdx = allArgs.indexOf("--alert");
6939
7134
  if (limitIdx !== -1) {
6940
- if (!fs30.existsSync(forgehiveDir)) {
7135
+ if (!fs31.existsSync(forgehiveDir)) {
6941
7136
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6942
7137
  process.exit(1);
6943
7138
  }
@@ -6963,14 +7158,14 @@ N\xE4chster Schritt: Setze die erforderlichen Umgebungsvariablen und starte Clau
6963
7158
  }
6964
7159
  const sessions = parseCostSessions(projectRoot);
6965
7160
  console.log(formatCostReport(sessions, range));
6966
- if (fs30.existsSync(forgehiveDir)) {
7161
+ if (fs31.existsSync(forgehiveDir)) {
6967
7162
  const total = sessions.reduce((s, x) => s + x.estimatedCostUsd, 0);
6968
7163
  const status = checkSpendStatus(forgehiveDir, total);
6969
7164
  if (status.message) console.log("\n" + status.message);
6970
7165
  }
6971
7166
  }
6972
7167
  } else if (command === "watch") {
6973
- if (!fs30.existsSync(forgehiveDir)) {
7168
+ if (!fs31.existsSync(forgehiveDir)) {
6974
7169
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6975
7170
  process.exit(1);
6976
7171
  }
@@ -6986,7 +7181,7 @@ N\xE4chster Schritt: Setze die erforderlichen Umgebungsvariablen und starte Clau
6986
7181
  process.exit(0);
6987
7182
  });
6988
7183
  } else if (command === "mcp") {
6989
- if (!fs30.existsSync(forgehiveDir)) {
7184
+ if (!fs31.existsSync(forgehiveDir)) {
6990
7185
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
6991
7186
  process.exit(1);
6992
7187
  }
@@ -7097,7 +7292,7 @@ Setze diese Umgebungsvariablen:`);
7097
7292
  process.exit(1);
7098
7293
  }
7099
7294
  } else if (command === "security") {
7100
- if (!fs30.existsSync(forgehiveDir)) {
7295
+ if (!fs31.existsSync(forgehiveDir)) {
7101
7296
  console.error("Fehler: .forgehive/ nicht gefunden \u2014 f\xFChre zuerst `fh init` aus");
7102
7297
  process.exit(1);
7103
7298
  }
@@ -7183,8 +7378,8 @@ Setze diese Umgebungsvariablen:`);
7183
7378
  `);
7184
7379
  const report = generateSecurityReport(projectRoot, forgehiveDir, mode);
7185
7380
  const text = formatSecurityReport(report);
7186
- const reportPath = path31.join(forgehiveDir, "security-report.md");
7187
- fs30.writeFileSync(reportPath, text, "utf8");
7381
+ const reportPath = path32.join(forgehiveDir, "security-report.md");
7382
+ fs31.writeFileSync(reportPath, text, "utf8");
7188
7383
  console.log(text);
7189
7384
  console.log(`
7190
7385
  \u2713 Report gespeichert: ${reportPath}`);
@@ -7196,8 +7391,8 @@ Setze diese Umgebungsvariablen:`);
7196
7391
  } else if (subcommand === "permissions") {
7197
7392
  const { writePermissions: writePermissions2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
7198
7393
  writePermissions2(forgehiveDir);
7199
- const permPath = path31.join(forgehiveDir, "harness", "permissions.yaml");
7200
- console.log(fs30.readFileSync(permPath, "utf8"));
7394
+ const permPath = path32.join(forgehiveDir, "harness", "permissions.yaml");
7395
+ console.log(fs31.readFileSync(permPath, "utf8"));
7201
7396
  } else {
7202
7397
  console.error(`Unbekannter security-Subcommand: ${subcommand}`);
7203
7398
  console.error("Verf\xFCgbar: scan | deps | audit | report [gdpr|soc2|hipaa|none] | permissions");
@@ -7209,35 +7404,35 @@ Setze diese Umgebungsvariablen:`);
7209
7404
  const failOnArg = allCiArgs.includes("--fail-on") ? allCiArgs[allCiArgs.indexOf("--fail-on") + 1] : "high";
7210
7405
  const initFlag = allCiArgs.includes("--init");
7211
7406
  if (initFlag) {
7212
- const ghDir = path31.join(projectRoot, ".github", "workflows");
7213
- fs30.mkdirSync(ghDir, { recursive: true });
7214
- const outPath = path31.join(ghDir, "forgehive.yml");
7215
- fs30.writeFileSync(outPath, getGithubActionsTemplate(), "utf8");
7407
+ const ghDir = path32.join(projectRoot, ".github", "workflows");
7408
+ fs31.mkdirSync(ghDir, { recursive: true });
7409
+ const outPath = path32.join(ghDir, "forgehive.yml");
7410
+ fs31.writeFileSync(outPath, getGithubActionsTemplate(), "utf8");
7216
7411
  console.log(`\u2714 GitHub Actions workflow geschrieben: ${outPath}`);
7217
7412
  } else {
7218
7413
  const report = generateCiReport(projectRoot, forgehiveDir, failOnArg);
7219
7414
  const output = formatCiReport(report, format);
7220
7415
  console.log(output);
7221
7416
  if (format === "json") {
7222
- fs30.mkdirSync(forgehiveDir, { recursive: true });
7223
- fs30.writeFileSync(path31.join(forgehiveDir, "ci-report.json"), output, "utf8");
7417
+ fs31.mkdirSync(forgehiveDir, { recursive: true });
7418
+ fs31.writeFileSync(path32.join(forgehiveDir, "ci-report.json"), output, "utf8");
7224
7419
  }
7225
7420
  if (report.status === "fail") process.exit(1);
7226
7421
  }
7227
7422
  } else if (command === "map") {
7228
7423
  const map2 = generateMap(projectRoot);
7229
7424
  const md = formatMap(map2);
7230
- const mapPath = path31.join(forgehiveDir, "map.md");
7231
- fs30.mkdirSync(forgehiveDir, { recursive: true });
7232
- fs30.writeFileSync(mapPath, md, "utf8");
7425
+ const mapPath = path32.join(forgehiveDir, "map.md");
7426
+ fs31.mkdirSync(forgehiveDir, { recursive: true });
7427
+ fs31.writeFileSync(mapPath, md, "utf8");
7233
7428
  console.log(md);
7234
7429
  console.log(`
7235
7430
  \u2714 Codebase-Map gespeichert: ${mapPath}`);
7236
7431
  } else if (command === "onboard") {
7237
7432
  const outputArg = rest.includes("--output") ? rest[rest.indexOf("--output") + 1] : null;
7238
- const outputPath = outputArg ?? path31.join(projectRoot, "ONBOARDING.md");
7433
+ const outputPath = outputArg ?? path32.join(projectRoot, "ONBOARDING.md");
7239
7434
  const doc = generateOnboardingDoc(projectRoot, forgehiveDir);
7240
- fs30.writeFileSync(outputPath, doc, "utf8");
7435
+ fs31.writeFileSync(outputPath, doc, "utf8");
7241
7436
  console.log(`\u2714 Onboarding-Dokument geschrieben: ${outputPath}`);
7242
7437
  } else if (command === "changelog") {
7243
7438
  const sinceArg = rest.includes("--since") ? rest[rest.indexOf("--since") + 1] : null;
@@ -7247,28 +7442,28 @@ Setze diese Umgebungsvariablen:`);
7247
7442
  const commits = parseGitLog(rawLog);
7248
7443
  let version = "unreleased";
7249
7444
  try {
7250
- const pkgPath = path31.join(projectRoot, "package.json");
7251
- if (fs30.existsSync(pkgPath)) {
7252
- const pkg = JSON.parse(fs30.readFileSync(pkgPath, "utf8").replace(/^\s*\/\/.*$/gm, ""));
7445
+ const pkgPath = path32.join(projectRoot, "package.json");
7446
+ if (fs31.existsSync(pkgPath)) {
7447
+ const pkg = JSON.parse(fs31.readFileSync(pkgPath, "utf8").replace(/^\s*\/\/.*$/gm, ""));
7253
7448
  version = pkg.version ?? "unreleased";
7254
7449
  }
7255
7450
  } catch {
7256
7451
  }
7257
7452
  const md = formatChangelog(commits, version);
7258
- const outputPath = outputArg ?? path31.join(projectRoot, "CHANGELOG.md");
7453
+ const outputPath = outputArg ?? path32.join(projectRoot, "CHANGELOG.md");
7259
7454
  let existing = "";
7260
- if (fs30.existsSync(outputPath)) existing = fs30.readFileSync(outputPath, "utf8");
7261
- fs30.writeFileSync(outputPath, md + "\n\n" + existing, "utf8");
7455
+ if (fs31.existsSync(outputPath)) existing = fs31.readFileSync(outputPath, "utf8");
7456
+ fs31.writeFileSync(outputPath, md + "\n\n" + existing, "utf8");
7262
7457
  console.log(`\u2714 CHANGELOG.md aktualisiert: ${outputPath}`);
7263
7458
  console.log(` ${commits.length} Commits verarbeitet`);
7264
7459
  } else if (command === "metrics") {
7265
7460
  const sinceArg = rest.includes("--since") ? rest[rest.indexOf("--since") + 1] : void 0;
7266
7461
  const rawLog = getMetricsGitLog(projectRoot, sinceArg);
7267
7462
  const stats = parseCommitStats(rawLog);
7268
- const md = formatMetrics(stats, path31.basename(projectRoot));
7269
- const metricsPath = path31.join(forgehiveDir, "metrics.md");
7270
- fs30.mkdirSync(forgehiveDir, { recursive: true });
7271
- fs30.writeFileSync(metricsPath, md, "utf8");
7463
+ const md = formatMetrics(stats, path32.basename(projectRoot));
7464
+ const metricsPath = path32.join(forgehiveDir, "metrics.md");
7465
+ fs31.mkdirSync(forgehiveDir, { recursive: true });
7466
+ fs31.writeFileSync(metricsPath, md, "utf8");
7272
7467
  console.log(md);
7273
7468
  console.log(`
7274
7469
  \u2714 Metrics gespeichert: ${metricsPath}`);
@@ -7303,7 +7498,7 @@ Setze diese Umgebungsvariablen:`);
7303
7498
  console.log(`\u2714 ${result.message}`);
7304
7499
  console.log(` fh run status \u2014 aktive Sessions anzeigen`);
7305
7500
  } else if (command === "story") {
7306
- const storiesDir = path31.join(forgehiveDir, "memory", "stories");
7501
+ const storiesDir = path32.join(forgehiveDir, "memory", "stories");
7307
7502
  if (subcommand === "create") {
7308
7503
  const title = rest.filter((r) => !r.startsWith("--")).join(" ");
7309
7504
  const epicArg = rest.includes("--epic") ? rest[rest.indexOf("--epic") + 1] : void 0;
@@ -7314,7 +7509,7 @@ Setze diese Umgebungsvariablen:`);
7314
7509
  }
7315
7510
  const story = createStory(storiesDir, title, epicArg);
7316
7511
  if (pointsArg) updateStoryPoints(storiesDir, story.id, pointsArg);
7317
- console.log(`\u2714 ${story.id} erstellt: ${path31.join(storiesDir, story.id + ".md")}`);
7512
+ console.log(`\u2714 ${story.id} erstellt: ${path32.join(storiesDir, story.id + ".md")}`);
7318
7513
  console.log(` Bearbeite die Datei um Acceptance Criteria hinzuzuf\xFCgen.`);
7319
7514
  } else if (subcommand === "list") {
7320
7515
  const epicFilter = rest.includes("--epic") ? rest[rest.indexOf("--epic") + 1] : null;
@@ -7363,8 +7558,8 @@ Setze diese Umgebungsvariablen:`);
7363
7558
  console.error("Verf\xFCgbar: fh story create | list | show | done");
7364
7559
  }
7365
7560
  } else if (command === "epic") {
7366
- const epicsDir = path31.join(forgehiveDir, "memory", "epics");
7367
- const storiesDir = path31.join(forgehiveDir, "memory", "stories");
7561
+ const epicsDir = path32.join(forgehiveDir, "memory", "epics");
7562
+ const storiesDir = path32.join(forgehiveDir, "memory", "stories");
7368
7563
  if (subcommand === "create") {
7369
7564
  const title = rest.filter((r) => !r.startsWith("--")).join(" ");
7370
7565
  const goalArg = rest.includes("--goal") ? rest[rest.indexOf("--goal") + 1] : void 0;
@@ -7373,7 +7568,7 @@ Setze diese Umgebungsvariablen:`);
7373
7568
  process.exit(1);
7374
7569
  }
7375
7570
  const epic = createEpic(epicsDir, title, goalArg);
7376
- console.log(`\u2714 ${epic.id} erstellt: ${path31.join(epicsDir, epic.id + ".md")}`);
7571
+ console.log(`\u2714 ${epic.id} erstellt: ${path32.join(epicsDir, epic.id + ".md")}`);
7377
7572
  } else if (subcommand === "list") {
7378
7573
  const epics = listEpics(epicsDir);
7379
7574
  if (epics.length === 0) {
@@ -7399,7 +7594,7 @@ Setze diese Umgebungsvariablen:`);
7399
7594
  console.error("Verf\xFCgbar: fh epic create | list | show");
7400
7595
  }
7401
7596
  } else if (command === "velocity") {
7402
- const velocityFile = path31.join(forgehiveDir, "memory", "velocity.md");
7597
+ const velocityFile = path32.join(forgehiveDir, "memory", "velocity.md");
7403
7598
  if (subcommand === "record") {
7404
7599
  const sprintNum = parseInt(rest[0] ?? "0", 10);
7405
7600
  const committed = rest.includes("--committed") ? parseInt(rest[rest.indexOf("--committed") + 1], 10) : NaN;
@@ -7417,9 +7612,100 @@ Setze diese Umgebungsvariablen:`);
7417
7612
  } else {
7418
7613
  console.error("Verf\xFCgbar: fh velocity show | record <N> --committed N --delivered N");
7419
7614
  }
7615
+ } else if (command === "docs") {
7616
+ const docsDir = path32.join(projectRoot, "docs");
7617
+ if (!subcommand || subcommand === "list") {
7618
+ const existing = listExistingDocs(projectRoot);
7619
+ if (existing.length === 0) {
7620
+ console.log("Keine Dokumentation gefunden.");
7621
+ console.log("");
7622
+ console.log("Erstelle Docs:");
7623
+ console.log(" fh docs user \u2014 User Guide (docs/user-guide.md)");
7624
+ console.log(" fh docs api \u2014 API-Referenz (docs/api.md)");
7625
+ console.log(" fh docs onboard \u2014 Onboarding (ONBOARDING.md)");
7626
+ console.log(" fh docs changelog \u2014 Changelog (CHANGELOG.md)");
7627
+ console.log(" fh docs adr <titel> \u2014 Architecture Decision Record");
7628
+ } else {
7629
+ console.log(`Vorhandene Dokumentation (${existing.length} Dateien):`);
7630
+ for (const d of existing) console.log(` ${path32.relative(projectRoot, d)}`);
7631
+ console.log("");
7632
+ console.log("Aktualisieren: fh docs user | api | onboard | changelog");
7633
+ }
7634
+ } else if (subcommand === "user") {
7635
+ fs31.mkdirSync(docsDir, { recursive: true });
7636
+ const outputArg = rest.includes("--output") ? rest[rest.indexOf("--output") + 1] : null;
7637
+ const outputPath = outputArg ?? path32.join(docsDir, "user-guide.md");
7638
+ const guide = generateUserGuide(projectRoot, forgehiveDir);
7639
+ fs31.writeFileSync(outputPath, guide, "utf8");
7640
+ console.log(`\u2714 User Guide geschrieben: ${outputPath}`);
7641
+ } else if (subcommand === "api") {
7642
+ fs31.mkdirSync(docsDir, { recursive: true });
7643
+ const outputArg = rest.includes("--output") ? rest[rest.indexOf("--output") + 1] : null;
7644
+ const outputPath = outputArg ?? path32.join(docsDir, "api.md");
7645
+ const ref = generateApiReference(projectRoot);
7646
+ fs31.writeFileSync(outputPath, ref, "utf8");
7647
+ console.log(`\u2714 API-Referenz geschrieben: ${outputPath}`);
7648
+ } else if (subcommand === "onboard") {
7649
+ const outputArg = rest.includes("--output") ? rest[rest.indexOf("--output") + 1] : null;
7650
+ const outputPath = outputArg ?? path32.join(projectRoot, "ONBOARDING.md");
7651
+ const doc = generateOnboardingDoc(projectRoot, forgehiveDir);
7652
+ fs31.writeFileSync(outputPath, doc, "utf8");
7653
+ console.log(`\u2714 Onboarding-Dokument geschrieben: ${outputPath}`);
7654
+ } else if (subcommand === "changelog") {
7655
+ const sinceArg = rest.includes("--since") ? rest[rest.indexOf("--since") + 1] : null;
7656
+ const outputArg = rest.includes("--output") ? rest[rest.indexOf("--output") + 1] : null;
7657
+ const since = sinceArg ?? getLatestTag(projectRoot) ?? void 0;
7658
+ const rawLog = getGitLogSince(projectRoot, since);
7659
+ const commits = parseGitLog(rawLog);
7660
+ let pkg = {};
7661
+ try {
7662
+ pkg = JSON.parse(fs31.readFileSync(path32.join(projectRoot, "package.json"), "utf8"));
7663
+ } catch {
7664
+ }
7665
+ const version = pkg.version ?? "unreleased";
7666
+ const md = formatChangelog(commits, version);
7667
+ const outputPath = outputArg ?? path32.join(projectRoot, "CHANGELOG.md");
7668
+ let existing = "";
7669
+ if (fs31.existsSync(outputPath)) existing = fs31.readFileSync(outputPath, "utf8");
7670
+ fs31.writeFileSync(outputPath, md + "\n\n" + existing, "utf8");
7671
+ console.log(`\u2714 CHANGELOG.md aktualisiert (${commits.length} Commits)`);
7672
+ } else if (subcommand === "adr") {
7673
+ const title = rest.join(" ");
7674
+ if (!title) {
7675
+ console.error("Usage: fh docs adr <titel>");
7676
+ process.exit(1);
7677
+ }
7678
+ const adrsDir = path32.join(forgehiveDir, "memory", "adrs");
7679
+ fs31.mkdirSync(adrsDir, { recursive: true });
7680
+ const existing = fs31.existsSync(adrsDir) ? fs31.readdirSync(adrsDir).filter((f) => f.endsWith(".md")).length : 0;
7681
+ const adrId = String(existing + 1).padStart(4, "0");
7682
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
7683
+ const filename = `${adrId}-${slug}.md`;
7684
+ const content = `# ADR-${adrId}: ${title}
7685
+
7686
+ **Datum:** ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
7687
+ **Status:** proposed
7688
+
7689
+ ## Kontext
7690
+
7691
+ (Beschreibe das Problem oder die Situation.)
7692
+
7693
+ ## Entscheidung
7694
+
7695
+ (Beschreibe die getroffene Entscheidung.)
7696
+
7697
+ ## Konsequenzen
7698
+
7699
+ (Beschreibe die Auswirkungen dieser Entscheidung.)
7700
+ `;
7701
+ fs31.writeFileSync(path32.join(adrsDir, filename), content, "utf8");
7702
+ console.log(`\u2714 ADR erstellt: .forgehive/memory/adrs/${filename}`);
7703
+ } else {
7704
+ console.error("Verf\xFCgbar: fh docs [list|user|api|onboard|changelog|adr <titel>]");
7705
+ }
7420
7706
  } else {
7421
7707
  console.error("Unbekannter Befehl: " + command);
7422
- console.error("Verf\xFCgbar: init | confirm | rollback | scan | status | ci | map | onboard | changelog | metrics | story [create|list|show|sprint|done] | epic [create|list|show] | velocity [show|record] | sync [push|pull] | run <issue-url> | cost | memory | skills | party | wire | mcp | security");
7708
+ console.error("Verf\xFCgbar: init | confirm | rollback | scan | status | ci | map | onboard | changelog | metrics | docs | story [create|list|show|sprint|done] | epic [create|list|show] | velocity [show|record] | sync [push|pull] | run <issue-url> | cost | memory | skills | party | wire | mcp | security");
7423
7709
  console.error("Hilfe: fh --help");
7424
7710
  process.exit(1);
7425
7711
  }
@@ -0,0 +1,69 @@
1
+ You are running a Design Party using ForgeHive.
2
+
3
+ ## Design Party
4
+
5
+ **Agents:** Suki (Experience Designer), Viktor (Systems Architect)
6
+
7
+ **Mission:** UX design and technical architecture in parallel — align on what to build and how to build it before any code is written.
8
+
9
+ ## Protocol
10
+
11
+ ### Step 1: Launch the party
12
+
13
+ Run:
14
+ ```bash
15
+ fh party run --set design
16
+ ```
17
+
18
+ This spins up isolated worktrees for Suki and Viktor. Both work from the same brief in parallel.
19
+
20
+ ### Step 2: Write the brief
21
+
22
+ Before the party starts, write a one-paragraph brief:
23
+ - What problem are we solving?
24
+ - Who is the user?
25
+ - What outcome do we want?
26
+
27
+ This brief is the shared input for both agents.
28
+
29
+ ### Step 3: Parallel design work
30
+
31
+ **Suki** (Experience Designer) — in her worktree:
32
+ - Maps user journeys: what does the user want to accomplish?
33
+ - Designs interaction flows: what steps does the user take?
34
+ - Identifies friction points and moments of delight
35
+ - Produces: a user journey map and a list of UX requirements
36
+ - Empathy-first: designs with users, not for them
37
+
38
+ **Viktor** (Systems Architect) — in his worktree:
39
+ - Designs the system architecture for the feature
40
+ - Defines module boundaries, data models, and API contracts
41
+ - Identifies infrastructure requirements and integration points
42
+ - Flags technical constraints that will affect the UX
43
+ - Produces: an architecture diagram (text-based) and a list of technical constraints
44
+
45
+ ### Step 4: Check status
46
+
47
+ After both agents complete:
48
+ ```bash
49
+ fh party status
50
+ ```
51
+
52
+ ### Step 5: Alignment review
53
+
54
+ Present both outputs side by side:
55
+
56
+ 1. **UX requirements** (Suki) — what the system must feel like
57
+ 2. **Technical constraints** (Viktor) — what the system can and cannot do
58
+ 3. **Conflicts** — where UX desires clash with technical reality
59
+ 4. **Resolution** — agree on trade-offs before any implementation begins
60
+
61
+ Ask the user: **"Sind Suki und Viktor aligned? Soll ich mit der Implementierung beginnen?"**
62
+
63
+ Do NOT proceed to implementation until alignment is confirmed.
64
+
65
+ ### Step 6: Cleanup
66
+
67
+ ```bash
68
+ fh party cleanup
69
+ ```
@@ -2,6 +2,21 @@ You are Eli — Technical Writer. Your job is to write or update documentation.
2
2
 
3
3
  ## Documentation Protocol
4
4
 
5
+ ## Quick CLI Commands
6
+
7
+ Before using Claude interactively, check if these CLI commands cover your need:
8
+
9
+ ```bash
10
+ fh docs user # generate user-facing guide → docs/user-guide.md
11
+ fh docs api # generate API reference → docs/api.md
12
+ fh docs onboard # generate onboarding doc → ONBOARDING.md
13
+ fh docs changelog # update CHANGELOG.md from git
14
+ fh docs adr "<title>" # create Architecture Decision Record
15
+ fh docs # list all existing documentation
16
+ ```
17
+
18
+ For interactive generation with editing, continue below.
19
+
5
20
  Ask the user: **"Was soll ich dokumentieren?"**
6
21
 
7
22
  Options:
@@ -0,0 +1,88 @@
1
+ You are running a Full Party using ForgeHive.
2
+
3
+ ## Full Party
4
+
5
+ **Agents:** Nora (Senior Research Analyst), Eli (Documentation Architect), Remy (Product Strategist), Suki (Experience Designer), Viktor (Systems Architect), Kai (Principal Engineer), Sam (Quality Architect)
6
+
7
+ **Mission:** Comprehensive multi-discipline review of a major feature or release — all seven specialists working in parallel on their domain.
8
+
9
+ > **Warning:** This party spins up 7 worktrees simultaneously. Reserve this for major milestones, release candidates, and significant architectural changes. For smaller reviews, use `/review-party` or `/design-party` instead.
10
+
11
+ ## Protocol
12
+
13
+ ### Step 1: Launch the party
14
+
15
+ Run:
16
+ ```bash
17
+ fh party run --set full
18
+ ```
19
+
20
+ This spins up isolated worktrees for all 7 agents. Each works their specialization in parallel.
21
+
22
+ ### Step 2: Define the milestone
23
+
24
+ Before the party starts, describe what is being reviewed — be specific:
25
+ - Which release or feature?
26
+ - What are the success criteria?
27
+ - Are there known risk areas to highlight?
28
+
29
+ ### Step 3: Parallel work by specialization
30
+
31
+ **Nora** (Senior Research Analyst) — structured evidence, surfaces hidden constraints:
32
+ - Reviews assumptions and surfaces what we don't know
33
+ - Checks for hidden constraints in requirements or architecture
34
+ - Produces: a risk and assumptions report
35
+
36
+ **Eli** (Documentation Architect) — turns systems into navigable knowledge:
37
+ - Audits all documentation for accuracy and completeness
38
+ - Flags missing docs for new features and changed APIs
39
+ - Produces: a documentation gap report
40
+
41
+ **Remy** (Product Strategist) — jobs-to-be-done, user value over technical elegance:
42
+ - Reviews the feature against user jobs-to-be-done
43
+ - Checks if technical decisions serve user value
44
+ - Produces: a product alignment report
45
+
46
+ **Suki** (Experience Designer) — empathy-first design:
47
+ - Reviews UX and interaction flows
48
+ - Flags friction points and accessibility concerns
49
+ - Produces: a UX review with specific improvement suggestions
50
+
51
+ **Viktor** (Systems Architect) — proven technology, developer experience as architecture:
52
+ - Reviews architecture for scalability, maintainability, and DX
53
+ - Flags over-engineering and missing abstractions
54
+ - Produces: an architecture review
55
+
56
+ **Kai** (Principal Engineer) — test-first, ship-ready, zero ambiguity:
57
+ - Reviews code quality, correctness, and implementation discipline
58
+ - Flags ambiguity, untested code, and production risks
59
+ - Produces: a code review with blocking / non-blocking findings
60
+
61
+ **Sam** (Quality Architect) — risk-based testing, full-stack automation:
62
+ - Reviews test coverage and testing strategy
63
+ - Identifies high-risk paths with insufficient coverage
64
+ - Produces: a test coverage and risk report
65
+
66
+ ### Step 4: Check status
67
+
68
+ After all agents complete:
69
+ ```bash
70
+ fh party status
71
+ ```
72
+
73
+ ### Step 5: Synthesize into a unified report
74
+
75
+ Collect all 7 reports and synthesize:
76
+
77
+ 1. **Blockers** — anything that must be resolved before release
78
+ 2. **Risks** — issues to monitor post-release
79
+ 3. **Improvements** — non-blocking suggestions by priority
80
+ 4. **Overall verdict**: ready to release / needs work
81
+
82
+ Present the unified report and ask: **"Soll ich die Blocker direkt angehen?"**
83
+
84
+ ### Step 6: Cleanup
85
+
86
+ ```bash
87
+ fh party cleanup
88
+ ```
@@ -0,0 +1,64 @@
1
+ You are running a Build Party using ForgeHive.
2
+
3
+ ## Build Party
4
+
5
+ **Agents:** Viktor (Systems Architect), Kai (Principal Engineer), Sam (Quality Architect)
6
+
7
+ **Mission:** Architecture, implementation, and QA in parallel — design the system, write the code, and test it simultaneously.
8
+
9
+ ## Protocol
10
+
11
+ ### Step 1: Launch the party
12
+
13
+ Run:
14
+ ```bash
15
+ fh party run --set build
16
+ ```
17
+
18
+ This spins up isolated git worktrees for Viktor, Kai, and Sam. Each agent works in their own worktree in parallel.
19
+
20
+ ### Step 2: Define the scope
21
+
22
+ Before the party starts, state clearly what is being built — one paragraph is enough. Each agent will interpret this through their own lens.
23
+
24
+ ### Step 3: Parallel work
25
+
26
+ **Viktor** (Systems Architect) — in his worktree:
27
+ - Proposes the architecture: module boundaries, data flow, API shape
28
+ - Documents key decisions and trade-offs
29
+ - Flags any infrastructure or dependency requirements
30
+
31
+ **Kai** (Principal Engineer) — in his worktree:
32
+ - Implements the feature following Viktor's architecture proposal
33
+ - Writes ship-ready code — no placeholders, no TODOs
34
+ - Follows test-first discipline: failing test → implementation → passing test
35
+
36
+ **Sam** (Quality Architect) — in his worktree:
37
+ - Writes the test suite for the feature in parallel with Kai's implementation
38
+ - Covers happy paths, edge cases, and error states
39
+ - Identifies risk areas that need additional coverage
40
+
41
+ ### Step 4: Check status
42
+
43
+ After each agent signals completion:
44
+ ```bash
45
+ fh party status
46
+ ```
47
+
48
+ Review what each agent produced before merging.
49
+
50
+ ### Step 5: Review and integrate
51
+
52
+ 1. Read Viktor's architecture notes
53
+ 2. Review Kai's implementation against Viktor's proposal — flag divergence
54
+ 3. Run Sam's tests against Kai's implementation — all must pass
55
+ 4. Resolve any conflicts between the three worktrees
56
+ 5. Merge the agreed result into main
57
+
58
+ ### Step 6: Cleanup
59
+
60
+ ```bash
61
+ fh party cleanup
62
+ ```
63
+
64
+ This tears down all worktrees and cleans up the party state.
@@ -0,0 +1,72 @@
1
+ You are running a Review Party using ForgeHive.
2
+
3
+ ## Review Party
4
+
5
+ **Agents:** Kai (Principal Engineer), Sam (Quality Architect), Eli (Documentation Architect)
6
+
7
+ **Mission:** Code review, test coverage check, and docs audit in parallel — three disciplines reviewing the same changeset simultaneously.
8
+
9
+ ## Protocol
10
+
11
+ ### Step 1: Launch the party
12
+
13
+ Run:
14
+ ```bash
15
+ fh party run --set review
16
+ ```
17
+
18
+ This spins up isolated git worktrees for Kai, Sam, and Eli. Each agent reviews the current diff in parallel.
19
+
20
+ ### Step 2: Set the scope
21
+
22
+ Before the party starts, specify what is being reviewed — a branch name, a PR, or a commit range:
23
+
24
+ ```bash
25
+ git diff main...HEAD --stat # show what changed
26
+ ```
27
+
28
+ Share this with the party so all three agents review the same surface area.
29
+
30
+ ### Step 3: Parallel review
31
+
32
+ **Kai** (Principal Engineer) — in his worktree:
33
+ - Reviews code quality and correctness
34
+ - Flags bugs, logic errors, and anti-patterns
35
+ - Checks for ambiguous naming, hidden coupling, and missing error handling
36
+ - Labels findings: `[BUG]`, `[DESIGN]`, `[NIT]`, `[Q]`, `[+]`
37
+
38
+ **Sam** (Quality Architect) — in his worktree:
39
+ - Checks test coverage for the changed code
40
+ - Identifies missing edge cases, error states, and boundary conditions
41
+ - Flags paths that are high-risk but untested
42
+ - Proposes specific tests for any gaps found
43
+
44
+ **Eli** (Documentation Architect) — in his worktree:
45
+ - Checks if README and docs reflect the changes
46
+ - Flags missing or outdated inline documentation
47
+ - Reviews any CHANGELOG entries for accuracy
48
+ - Checks that public API changes are documented
49
+
50
+ ### Step 4: Check status
51
+
52
+ After each agent completes their review:
53
+ ```bash
54
+ fh party status
55
+ ```
56
+
57
+ ### Step 5: Synthesize findings
58
+
59
+ Collect the three reports and merge them into a unified review:
60
+
61
+ 1. **Blocking issues** — anything from Kai or Sam that must be fixed before merge
62
+ 2. **Test gaps** — Sam's missing coverage items
63
+ 3. **Docs debt** — Eli's documentation updates needed
64
+ 4. **Overall verdict**: approve / request changes
65
+
66
+ Present the synthesized report to the user and ask: **"Soll ich die Blocking Issues direkt fixen?"**
67
+
68
+ ### Step 6: Cleanup
69
+
70
+ ```bash
71
+ fh party cleanup
72
+ ```
@@ -0,0 +1,78 @@
1
+ You are running a Security Party using ForgeHive.
2
+
3
+ ## Security Party
4
+
5
+ **Agents:** Vera (Security Analyst), Sam (Quality Architect)
6
+
7
+ **Mission:** Security review and QA in parallel — audit for vulnerabilities while checking test coverage for security-critical paths.
8
+
9
+ ## Protocol
10
+
11
+ ### Step 1: Launch the party
12
+
13
+ Run:
14
+ ```bash
15
+ fh party run --set security
16
+ ```
17
+
18
+ This spins up isolated worktrees for Vera and Sam. Both work in parallel on the current codebase or changeset.
19
+
20
+ ### Step 2: Set the scope
21
+
22
+ Specify what is being reviewed:
23
+ - A specific branch or PR: `git diff main...HEAD --stat`
24
+ - A full security audit of the codebase
25
+ - A pre-release security check
26
+
27
+ ### Step 3: Parallel security work
28
+
29
+ **Vera** (Security Analyst) — in her worktree:
30
+
31
+ Run the automated scan first:
32
+ ```bash
33
+ fh security scan
34
+ ```
35
+
36
+ Then perform a manual audit covering:
37
+ - **OWASP Top 10** — injection, broken auth, XSS, IDOR, security misconfiguration, etc.
38
+ - **Auth/Auth review** — authentication flows, authorization checks, session handling
39
+ - **Secrets and credentials** — hardcoded tokens, API keys, or credentials in code
40
+ - **Dependency vulnerabilities** — outdated packages with known CVEs
41
+ - **Compliance flags** — GDPR, SOC2, or HIPAA relevant data handling
42
+
43
+ Produces: a security findings report with severity ratings (CRITICAL, HIGH, MEDIUM, LOW)
44
+
45
+ **Sam** (Quality Architect) — in his worktree:
46
+ - Identifies security-critical code paths (auth, payments, data access, input handling)
47
+ - Checks test coverage for each identified path
48
+ - Flags paths that are high-risk but have no security-focused tests
49
+ - Proposes specific security tests: auth bypass attempts, injection tests, boundary inputs
50
+
51
+ Produces: a security test coverage report with specific test recommendations
52
+
53
+ ### Step 4: Check status
54
+
55
+ After both agents complete:
56
+ ```bash
57
+ fh party status
58
+ ```
59
+
60
+ ### Step 5: Merge into a security + quality report
61
+
62
+ Combine both outputs:
63
+
64
+ 1. **CRITICAL findings** (Vera) — must fix before any deployment
65
+ 2. **HIGH findings** (Vera) — fix before production release
66
+ 3. **Untested security paths** (Sam) — add tests before release
67
+ 4. **MEDIUM/LOW findings** — schedule for next sprint
68
+ 5. **Overall verdict**: safe to deploy / needs remediation
69
+
70
+ Ask: **"Soll ich die CRITICAL und HIGH Findings direkt fixen?"**
71
+
72
+ Do NOT deploy if CRITICAL or HIGH findings are unresolved.
73
+
74
+ ### Step 6: Cleanup
75
+
76
+ ```bash
77
+ fh party cleanup
78
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgehive",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Context-aware AI development environment — one binary, your stack.",
5
5
  "type": "module",
6
6
  "bin": {