forgedock 1.0.11 → 1.0.12

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/bin/forgedock.mjs +94 -8
  2. package/package.json +1 -1
package/bin/forgedock.mjs CHANGED
@@ -36,6 +36,42 @@ const YELLOW = "\x1b[33m";
36
36
  const CYAN = "\x1b[36m";
37
37
  const RED = "\x1b[31m";
38
38
  const RESET = "\x1b[0m";
39
+ const DIM = "\x1b[2m";
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Splash — the ForgeDock wordmark shown at startup
43
+ // ---------------------------------------------------------------------------
44
+
45
+ const LOGO_LINES = [
46
+ " ╔═╗╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗╔═╗╦╔═",
47
+ " ╠╣ ║ ║╠╦╝║ ╦║╣ ║║║ ║║ ╠╩╗",
48
+ " ╚ ╚═╝╩╚═╚═╝╚═╝═╩╝╚═╝╚═╝╩ ╩",
49
+ ];
50
+
51
+ function getVersion() {
52
+ try {
53
+ const pkg = JSON.parse(
54
+ readFileSync(join(FORGE_HOME, "package.json"), "utf-8"),
55
+ );
56
+ return pkg.version || "";
57
+ } catch {
58
+ return "";
59
+ }
60
+ }
61
+
62
+ function splash() {
63
+ const version = getVersion();
64
+ console.log("");
65
+ for (const line of LOGO_LINES) {
66
+ console.log(`${BOLD}${CYAN}${line}${RESET}`);
67
+ }
68
+ console.log("");
69
+ console.log(` ${DIM}GitHub as a knowledge graph for AI agents${RESET}`);
70
+ if (version) {
71
+ console.log(` ${DIM}v${version}${RESET}`);
72
+ }
73
+ console.log("");
74
+ }
39
75
 
40
76
  /**
41
77
  * Prompt the user for a yes/no confirmation.
@@ -55,11 +91,34 @@ async function confirmOverwrite(question) {
55
91
  });
56
92
  rl.question(question, (answer) => {
57
93
  rl.close();
58
- resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
94
+ resolve(
95
+ answer.trim().toLowerCase() === "y" ||
96
+ answer.trim().toLowerCase() === "yes",
97
+ );
59
98
  });
60
99
  });
61
100
  }
62
101
 
102
+ /**
103
+ * Returns true if `dir` is inside a git work tree. Used to avoid generating
104
+ * forge.yaml in non-project directories — e.g. when npx defaults cwd to a
105
+ * system folder (C:\Windows) because the launching shell sat on an
106
+ * unsupported UNC/WSL path. The command is a fixed string with no
107
+ * interpolated input, so there is no shell-injection surface (ref #151).
108
+ */
109
+ function isGitWorkTree(dir) {
110
+ try {
111
+ const out = execSync("git rev-parse --is-inside-work-tree", {
112
+ cwd: dir,
113
+ stdio: ["ignore", "pipe", "ignore"],
114
+ encoding: "utf-8",
115
+ });
116
+ return out.trim() === "true";
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
63
122
  async function findMarkdownFiles(dir) {
64
123
  const results = [];
65
124
  const entries = await readdir(dir, { withFileTypes: true });
@@ -159,10 +218,24 @@ async function install() {
159
218
  );
160
219
  console.log("");
161
220
 
162
- // Auto-generate forge.yaml if missing — no second command needed
163
- const forgeYamlPath = join(process.cwd(), "forge.yaml");
221
+ // Auto-generate forge.yaml if missing — no second command needed.
222
+ // Only do this inside a real git project; otherwise cwd may be a system or
223
+ // read-only directory (e.g. C:\Windows when npx is launched from a WSL path),
224
+ // and writing forge.yaml there would crash. See #585.
225
+ const cwd = process.cwd();
226
+ const forgeYamlPath = join(cwd, "forge.yaml");
164
227
  if (!existsSync(forgeYamlPath)) {
165
- await init(true);
228
+ if (isGitWorkTree(cwd)) {
229
+ await init(true);
230
+ } else {
231
+ console.log(
232
+ ` ${YELLOW}Skipping forge.yaml generation${RESET} — current directory is not a git project.`,
233
+ );
234
+ console.log(
235
+ ` Run ${CYAN}npx forgedock init${RESET} inside your project directory to generate it.`,
236
+ );
237
+ console.log("");
238
+ }
166
239
  }
167
240
  }
168
241
 
@@ -511,15 +584,15 @@ project:
511
584
  name: "${projectName}"
512
585
  owner: "${owner}"
513
586
  repo: "${repo}"
514
- description: "${description.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"
587
+ description: "${description.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"
515
588
 
516
589
  # =============================================================================
517
590
  # PATHS (REQUIRED)
518
591
  # =============================================================================
519
592
 
520
593
  paths:
521
- root: "${cwd.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"
522
- worktree_base: "${worktreeBase.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"
594
+ root: "${cwd.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"
595
+ worktree_base: "${worktreeBase.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"
523
596
 
524
597
  # =============================================================================
525
598
  # BRANCHES (REQUIRED)
@@ -583,7 +656,18 @@ branches:
583
656
  # - '"status": "ok"'
584
657
  `;
585
658
 
586
- writeFileSync(outputPath, content, "utf-8");
659
+ try {
660
+ writeFileSync(outputPath, content, "utf-8");
661
+ } catch (err) {
662
+ console.error("");
663
+ console.error(
664
+ ` ${RED}Error${RESET}: Could not write forge.yaml to ${outputPath} — ${err.code ?? err.message}`,
665
+ );
666
+ console.error(
667
+ ` Run ${CYAN}npx forgedock init${RESET} from a writable project directory.`,
668
+ );
669
+ process.exit(1);
670
+ }
587
671
 
588
672
  console.log(` ${GREEN}Created${RESET}: forge.yaml`);
589
673
  console.log("");
@@ -641,6 +725,8 @@ function help() {
641
725
  console.log("");
642
726
  }
643
727
 
728
+ splash();
729
+
644
730
  switch (command) {
645
731
  case "install":
646
732
  await install();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgedock",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "GitHub as a knowledge graph for AI agents. Autonomous dev pipeline for Claude Code.",
5
5
  "bin": {
6
6
  "forgedock": "bin/forgedock.mjs"