@gitpagedocs/cli 1.1.53 → 1.1.54

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/index.mjs CHANGED
@@ -1,38 +1,38 @@
1
- #!/usr/bin/env node
2
-
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { spawnSync } from "node:child_process";
6
-
7
- const scriptPath = fileURLToPath(import.meta.url);
8
- const scriptDir = path.dirname(scriptPath);
9
- const tsEntry = path.join(scriptDir, "presentation", "index.ts");
10
-
11
- // Resolve tsx from THIS package's location, not the user's cwd. `node --import
12
- // tsx` would otherwise resolve the bare "tsx" specifier relative to the current
13
- // working directory, which fails when the CLI is installed globally and run
14
- // from a project that doesn't depend on tsx (ERR_MODULE_NOT_FOUND 'tsx').
15
- let tsxLoader = "tsx";
16
- try {
17
- tsxLoader = import.meta.resolve("tsx");
18
- } catch {
19
- // Older Node without a stable import.meta.resolve — fall back to the bare
20
- // specifier (works when tsx is resolvable from the cwd or globally).
21
- }
22
-
23
- const result = spawnSync(
24
- process.execPath,
25
- ["--import", tsxLoader, tsEntry, ...process.argv.slice(2)],
26
- {
27
- stdio: "inherit",
28
- cwd: process.cwd(),
29
- env: process.env,
30
- },
31
- );
32
-
33
- if (result.error) {
34
- console.error("[gitpagedocs] Failed to run TypeScript CLI runtime.", result.error);
35
- process.exitCode = 1;
36
- } else if (typeof result.status === "number" && result.status !== 0) {
37
- process.exitCode = result.status;
38
- }
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { spawnSync } from "node:child_process";
6
+
7
+ const scriptPath = fileURLToPath(import.meta.url);
8
+ const scriptDir = path.dirname(scriptPath);
9
+ const tsEntry = path.join(scriptDir, "presentation", "index.ts");
10
+
11
+ // Resolve tsx from THIS package's location, not the user's cwd. `node --import
12
+ // tsx` would otherwise resolve the bare "tsx" specifier relative to the current
13
+ // working directory, which fails when the CLI is installed globally and run
14
+ // from a project that doesn't depend on tsx (ERR_MODULE_NOT_FOUND 'tsx').
15
+ let tsxLoader = "tsx";
16
+ try {
17
+ tsxLoader = import.meta.resolve("tsx");
18
+ } catch {
19
+ // Older Node without a stable import.meta.resolve — fall back to the bare
20
+ // specifier (works when tsx is resolvable from the cwd or globally).
21
+ }
22
+
23
+ const result = spawnSync(
24
+ process.execPath,
25
+ ["--import", tsxLoader, tsEntry, ...process.argv.slice(2)],
26
+ {
27
+ stdio: "inherit",
28
+ cwd: process.cwd(),
29
+ env: process.env,
30
+ },
31
+ );
32
+
33
+ if (result.error) {
34
+ console.error("[gitpagedocs] Failed to run TypeScript CLI runtime.", result.error);
35
+ process.exitCode = 1;
36
+ } else if (typeof result.status === "number" && result.status !== 0) {
37
+ process.exitCode = result.status;
38
+ }
@@ -1,47 +1,47 @@
1
- import fs from "node:fs/promises";
2
- import { defaultConfigLoader } from "@gitpagedocs/tools";
3
-
4
- export interface DocsAccessConfig {
5
- enabled: boolean;
6
- publicKey: string;
7
- }
8
-
9
- /**
10
- * Reads and patches the generated gitpagedocs/config.json IN PLACE, preserving
11
- * every existing key (raw-JSON round-trip — it never reserializes through a typed
12
- * model that could drop unknown fields). Used by interactive commands that need
13
- * to persist a single value (e.g. the documentation password public key).
14
- */
15
- export class GitPageDocsConfigRepository {
16
- constructor(private readonly cwd: string = process.cwd()) {}
17
-
18
- /** Absolute path to the JSON config, or throws a friendly, actionable error. */
19
- async resolvePath(): Promise<string> {
20
- const resolved = await defaultConfigLoader.resolveConfigPath(this.cwd);
21
- if (!resolved) {
22
- throw new Error("gitpagedocs config.json not found. Run `gitpagedocs` first to generate it.");
23
- }
24
- if (!resolved.endsWith(".json")) {
25
- throw new Error(`Expected a JSON config to patch, found "${resolved}". Use a gitpagedocs/config.json.`);
26
- }
27
- return resolved;
28
- }
29
-
30
- /**
31
- * Set `site.docsAccess = { enabled, publicKey }`, leaving all other config keys
32
- * untouched. Returns the path that was written.
33
- */
34
- async patchDocsAccess(publicKey: string, enabled = true): Promise<string> {
35
- const configPath = await this.resolvePath();
36
- const raw = await fs.readFile(configPath, "utf-8");
37
- const config = JSON.parse(raw) as Record<string, unknown>;
38
- const site =
39
- config.site && typeof config.site === "object"
40
- ? (config.site as Record<string, unknown>)
41
- : {};
42
- site.docsAccess = { enabled, publicKey } satisfies DocsAccessConfig;
43
- config.site = site;
44
- await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
45
- return configPath;
46
- }
47
- }
1
+ import fs from "node:fs/promises";
2
+ import { defaultConfigLoader } from "@gitpagedocs/tools";
3
+
4
+ export interface DocsAccessConfig {
5
+ enabled: boolean;
6
+ publicKey: string;
7
+ }
8
+
9
+ /**
10
+ * Reads and patches the generated gitpagedocs/config.json IN PLACE, preserving
11
+ * every existing key (raw-JSON round-trip — it never reserializes through a typed
12
+ * model that could drop unknown fields). Used by interactive commands that need
13
+ * to persist a single value (e.g. the documentation password public key).
14
+ */
15
+ export class GitPageDocsConfigRepository {
16
+ constructor(private readonly cwd: string = process.cwd()) {}
17
+
18
+ /** Absolute path to the JSON config, or throws a friendly, actionable error. */
19
+ async resolvePath(): Promise<string> {
20
+ const resolved = await defaultConfigLoader.resolveConfigPath(this.cwd);
21
+ if (!resolved) {
22
+ throw new Error("gitpagedocs config.json not found. Run `gitpagedocs` first to generate it.");
23
+ }
24
+ if (!resolved.endsWith(".json")) {
25
+ throw new Error(`Expected a JSON config to patch, found "${resolved}". Use a gitpagedocs/config.json.`);
26
+ }
27
+ return resolved;
28
+ }
29
+
30
+ /**
31
+ * Set `site.docsAccess = { enabled, publicKey }`, leaving all other config keys
32
+ * untouched. Returns the path that was written.
33
+ */
34
+ async patchDocsAccess(publicKey: string, enabled = true): Promise<string> {
35
+ const configPath = await this.resolvePath();
36
+ const raw = await fs.readFile(configPath, "utf-8");
37
+ const config = JSON.parse(raw) as Record<string, unknown>;
38
+ const site =
39
+ config.site && typeof config.site === "object"
40
+ ? (config.site as Record<string, unknown>)
41
+ : {};
42
+ site.docsAccess = { enabled, publicKey } satisfies DocsAccessConfig;
43
+ config.site = site;
44
+ await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
45
+ return configPath;
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
- {
2
- "name": "@gitpagedocs/cli",
3
- "version": "1.1.53",
4
- "description": "CLI that scaffolds and maintains gitpagedocs documentation, generates docs with AI, configures GitHub Pages, and runs the MCP server.",
5
- "bin": {
6
- "gitpagedocs": "index.mjs"
7
- },
8
- "main": "index.mjs",
9
- "files": [
10
- "index.mjs",
11
- "start.mjs",
12
- "ai",
13
- "application",
14
- "builders",
15
- "content",
16
- "contracts",
17
- "data",
18
- "domain",
19
- "home",
20
- "infrastructure",
21
- "presentation",
22
- "runtime",
23
- "ui",
24
- "prebuilt",
25
- "README.md"
26
- ],
27
- "engines": {
28
- "node": ">=20"
29
- },
30
- "dependencies": {
31
- "@clack/prompts": "^1.5.1",
32
- "@gitpagedocs/mcp": "^1.1.53",
33
- "@gitpagedocs/tools": "^1.1.53",
34
- "tsx": "^4.21.0"
35
- },
36
- "repository": {
37
- "type": "git",
38
- "url": "git+https://github.com/Vidigal-code/git-page-docs.git",
39
- "directory": "cli"
40
- },
41
- "keywords": [
42
- "gitpagedocs",
43
- "documentation",
44
- "markdown",
45
- "spa",
46
- "cli",
47
- "github-pages",
48
- "create-gitpagedocs"
49
- ],
50
- "author": "Vidigal-code",
51
- "license": "ISC",
52
- "bugs": {
53
- "url": "https://github.com/Vidigal-code/git-page-docs/issues"
54
- },
55
- "homepage": "https://vidigal-code.github.io/git-page-docs/",
56
- "publishConfig": {
57
- "registry": "https://registry.npmjs.org/",
58
- "access": "public"
59
- }
60
- }
1
+ {
2
+ "name": "@gitpagedocs/cli",
3
+ "version": "1.1.54",
4
+ "description": "CLI that scaffolds and maintains gitpagedocs documentation, generates docs with AI, configures GitHub Pages, and runs the MCP server.",
5
+ "bin": {
6
+ "gitpagedocs": "index.mjs"
7
+ },
8
+ "main": "index.mjs",
9
+ "files": [
10
+ "index.mjs",
11
+ "start.mjs",
12
+ "ai",
13
+ "application",
14
+ "builders",
15
+ "content",
16
+ "contracts",
17
+ "data",
18
+ "domain",
19
+ "home",
20
+ "infrastructure",
21
+ "presentation",
22
+ "runtime",
23
+ "ui",
24
+ "prebuilt",
25
+ "README.md"
26
+ ],
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "dependencies": {
31
+ "@clack/prompts": "^1.5.1",
32
+ "@gitpagedocs/mcp": "^1.1.53",
33
+ "@gitpagedocs/tools": "^1.1.53",
34
+ "tsx": "^4.21.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/Vidigal-code/git-page-docs.git",
39
+ "directory": "cli"
40
+ },
41
+ "keywords": [
42
+ "gitpagedocs",
43
+ "documentation",
44
+ "markdown",
45
+ "spa",
46
+ "cli",
47
+ "github-pages",
48
+ "create-gitpagedocs"
49
+ ],
50
+ "author": "Vidigal-code",
51
+ "license": "ISC",
52
+ "bugs": {
53
+ "url": "https://github.com/Vidigal-code/git-page-docs/issues"
54
+ },
55
+ "homepage": "https://vidigal-code.github.io/git-page-docs/",
56
+ "publishConfig": {
57
+ "registry": "https://registry.npmjs.org/",
58
+ "access": "public"
59
+ }
60
+ }
@@ -1,62 +1,62 @@
1
- import { NodeCryptoService, deriveDocAccessKeys } from "@gitpagedocs/tools";
2
- import type { CommandContext } from "./run-command";
3
- import { askPassword, note, outro } from "../ui/clack";
4
- import { interactivePromptsAvailable } from "../ui/prompts";
5
- import { GitPageDocsConfigRepository } from "../../infrastructure/gitpagedocs-config-file";
6
-
7
- const MIN_PASSWORD_LENGTH = 4;
8
-
9
- /**
10
- * `gitpagedocs password` — interactively set a documentation access password.
11
- *
12
- * Type + confirm a password, derive the double-hash key pair, write the PUBLIC
13
- * key into gitpagedocs/config.json (which makes the frontend gate the docs), and
14
- * print the PRIVATE key for the user to copy. The password itself is never stored.
15
- */
16
- export async function runPassword(ctx: CommandContext): Promise<void> {
17
- if (!interactivePromptsAvailable()) {
18
- // eslint-disable-next-line no-console
19
- console.log("\n `gitpagedocs password` is interactive — run it in a terminal (TTY).\n");
20
- return;
21
- }
22
-
23
- const password = await askPassword({
24
- message: "Documentation password:",
25
- validate: (v) =>
26
- v && v.trim().length >= MIN_PASSWORD_LENGTH
27
- ? undefined
28
- : `Use at least ${MIN_PASSWORD_LENGTH} characters.`,
29
- });
30
- const confirmation = await askPassword({
31
- message: "Confirm password:",
32
- validate: (v) => (v ? undefined : "Required."),
33
- });
34
-
35
- if (password !== confirmation) {
36
- note("Passwords do not match. Nothing was changed.", "Aborted");
37
- return;
38
- }
39
-
40
- const { privateKey, publicKey } = await deriveDocAccessKeys(password, new NodeCryptoService());
41
-
42
- let configPath: string;
43
- try {
44
- configPath = await new GitPageDocsConfigRepository(ctx.cwd).patchDocsAccess(publicKey);
45
- } catch (error) {
46
- note(error instanceof Error ? error.message : String(error), "Could not save");
47
- return;
48
- }
49
-
50
- note(
51
- `Public key saved to ${configPath}\nThe documentation is now password-protected.`,
52
- "Saved",
53
- );
54
- // eslint-disable-next-line no-console
55
- console.log(
56
- "\n PRIVATE KEY (copy & keep it safe — it also unlocks the docs):\n\n" +
57
- ` ${privateKey}\n\n` +
58
- " Readers can unlock with the password OR this private key.\n" +
59
- " To remove protection, set site.docsAccess.enabled to false in config.json.\n",
60
- );
61
- outro("Done.");
62
- }
1
+ import { NodeCryptoService, deriveDocAccessKeys } from "@gitpagedocs/tools";
2
+ import type { CommandContext } from "./run-command";
3
+ import { askPassword, note, outro } from "../ui/clack";
4
+ import { interactivePromptsAvailable } from "../ui/prompts";
5
+ import { GitPageDocsConfigRepository } from "../../infrastructure/gitpagedocs-config-file";
6
+
7
+ const MIN_PASSWORD_LENGTH = 4;
8
+
9
+ /**
10
+ * `gitpagedocs password` — interactively set a documentation access password.
11
+ *
12
+ * Type + confirm a password, derive the double-hash key pair, write the PUBLIC
13
+ * key into gitpagedocs/config.json (which makes the frontend gate the docs), and
14
+ * print the PRIVATE key for the user to copy. The password itself is never stored.
15
+ */
16
+ export async function runPassword(ctx: CommandContext): Promise<void> {
17
+ if (!interactivePromptsAvailable()) {
18
+ // eslint-disable-next-line no-console
19
+ console.log("\n `gitpagedocs password` is interactive — run it in a terminal (TTY).\n");
20
+ return;
21
+ }
22
+
23
+ const password = await askPassword({
24
+ message: "Documentation password:",
25
+ validate: (v) =>
26
+ v && v.trim().length >= MIN_PASSWORD_LENGTH
27
+ ? undefined
28
+ : `Use at least ${MIN_PASSWORD_LENGTH} characters.`,
29
+ });
30
+ const confirmation = await askPassword({
31
+ message: "Confirm password:",
32
+ validate: (v) => (v ? undefined : "Required."),
33
+ });
34
+
35
+ if (password !== confirmation) {
36
+ note("Passwords do not match. Nothing was changed.", "Aborted");
37
+ return;
38
+ }
39
+
40
+ const { privateKey, publicKey } = await deriveDocAccessKeys(password, new NodeCryptoService());
41
+
42
+ let configPath: string;
43
+ try {
44
+ configPath = await new GitPageDocsConfigRepository(ctx.cwd).patchDocsAccess(publicKey);
45
+ } catch (error) {
46
+ note(error instanceof Error ? error.message : String(error), "Could not save");
47
+ return;
48
+ }
49
+
50
+ note(
51
+ `Public key saved to ${configPath}\nThe documentation is now password-protected.`,
52
+ "Saved",
53
+ );
54
+ // eslint-disable-next-line no-console
55
+ console.log(
56
+ "\n PRIVATE KEY (copy & keep it safe — it also unlocks the docs):\n\n" +
57
+ ` ${privateKey}\n\n` +
58
+ " Readers can unlock with the password OR this private key.\n" +
59
+ " To remove protection, set site.docsAccess.enabled to false in config.json.\n",
60
+ );
61
+ outro("Done.");
62
+ }
@@ -1,48 +1,48 @@
1
- import type { CliOptions } from "../../domain/models/cli-options";
2
- import { parseCliOptions } from "./parser";
3
- import {
4
- shouldRunInteractive,
5
- promptConfigOnlyOptions,
6
- promptHomeOptions,
7
- promptDeployOptions,
8
- ensureGitRepoInteractive,
9
- interactivePromptsAvailable,
10
- } from "../ui/prompts";
11
- import { DEFAULTS } from "./schema";
12
- // @ts-expect-error .mjs runtime module is type-less in this package.
13
- import { detectRepoFromGit } from "../../runtime/git-ops.mjs";
14
-
15
- export async function resolveOptions(argv: string[], env: NodeJS.ProcessEnv): Promise<CliOptions> {
16
- const parsed = parseCliOptions(argv, env);
17
-
18
- if (parsed.mode === "home") {
19
- parsed.repositorySearch = parsed.repositorySearch ?? DEFAULTS.home.repositorySearch;
20
- parsed.basePath = parsed.basePath ?? parsed.docsPath ?? DEFAULTS.home.basePath;
21
- }
22
-
23
- // `deploy` / `--push` need owner + repo AND a git repo. If anything is
24
- // missing, prompt interactively (owner/repo pre-filled from the git origin
25
- // remote; offer to `git init`) instead of throwing. In CI/non-TTY we fall
26
- // through so the explicit guards in the push flow still fire clearly.
27
- if (parsed.shouldPush && interactivePromptsAvailable()) {
28
- let resolved = parsed;
29
- if (!parsed.githubOwner || !parsed.githubRepo) {
30
- const detected = detectRepoFromGit(process.cwd()) as { owner: string; repo: string } | null;
31
- resolved = await promptDeployOptions(parsed, detected);
32
- }
33
- await ensureGitRepoInteractive(process.cwd());
34
- return resolved;
35
- }
36
-
37
- const runHomeInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "home");
38
- if (runHomeInteractive && parsed.mode === "home") {
39
- return promptHomeOptions(parsed);
40
- }
41
-
42
- const runConfigInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "config-only");
43
- if (runConfigInteractive && parsed.mode === "config-only") {
44
- return promptConfigOnlyOptions(parsed);
45
- }
46
-
47
- return parsed;
48
- }
1
+ import type { CliOptions } from "../../domain/models/cli-options";
2
+ import { parseCliOptions } from "./parser";
3
+ import {
4
+ shouldRunInteractive,
5
+ promptConfigOnlyOptions,
6
+ promptHomeOptions,
7
+ promptDeployOptions,
8
+ ensureGitRepoInteractive,
9
+ interactivePromptsAvailable,
10
+ } from "../ui/prompts";
11
+ import { DEFAULTS } from "./schema";
12
+ // @ts-expect-error .mjs runtime module is type-less in this package.
13
+ import { detectRepoFromGit } from "../../runtime/git-ops.mjs";
14
+
15
+ export async function resolveOptions(argv: string[], env: NodeJS.ProcessEnv): Promise<CliOptions> {
16
+ const parsed = parseCliOptions(argv, env);
17
+
18
+ if (parsed.mode === "home") {
19
+ parsed.repositorySearch = parsed.repositorySearch ?? DEFAULTS.home.repositorySearch;
20
+ parsed.basePath = parsed.basePath ?? parsed.docsPath ?? DEFAULTS.home.basePath;
21
+ }
22
+
23
+ // `deploy` / `--push` need owner + repo AND a git repo. If anything is
24
+ // missing, prompt interactively (owner/repo pre-filled from the git origin
25
+ // remote; offer to `git init`) instead of throwing. In CI/non-TTY we fall
26
+ // through so the explicit guards in the push flow still fire clearly.
27
+ if (parsed.shouldPush && interactivePromptsAvailable()) {
28
+ let resolved = parsed;
29
+ if (!parsed.githubOwner || !parsed.githubRepo) {
30
+ const detected = detectRepoFromGit(process.cwd()) as { owner: string; repo: string } | null;
31
+ resolved = await promptDeployOptions(parsed, detected);
32
+ }
33
+ await ensureGitRepoInteractive(process.cwd());
34
+ return resolved;
35
+ }
36
+
37
+ const runHomeInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "home");
38
+ if (runHomeInteractive && parsed.mode === "home") {
39
+ return promptHomeOptions(parsed);
40
+ }
41
+
42
+ const runConfigInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "config-only");
43
+ if (runConfigInteractive && parsed.mode === "config-only") {
44
+ return promptConfigOnlyOptions(parsed);
45
+ }
46
+
47
+ return parsed;
48
+ }
@@ -14,6 +14,7 @@ export async function ensureGitHubPagesWorkflow(getCurrentGitBranch, writeText,
14
14
  env:
15
15
  GITHUB_ACTIONS: "true"
16
16
  GITHUB_REPOSITORY: \${{ github.repository }}
17
+ GITPAGEDOCS_REPOSITORY_SEARCH: "true"
17
18
  GITPAGEDOCS_PATH: "${pathSegment}"
18
19
 
19
20
  - name: Relocate output to custom docs path
@@ -27,7 +28,8 @@ export async function ensureGitHubPagesWorkflow(getCurrentGitBranch, writeText,
27
28
  working-directory: .gitpagedocs-runtime
28
29
  env:
29
30
  GITHUB_ACTIONS: "true"
30
- GITHUB_REPOSITORY: \${{ github.repository }}`;
31
+ GITHUB_REPOSITORY: \${{ github.repository }}
32
+ GITPAGEDOCS_REPOSITORY_SEARCH: "true"`;
31
33
  const currentBranch = getCurrentGitBranch();
32
34
  const workflowPath = ".github/workflows/gitpagedocs-pages.yml";
33
35
  const workflowContent = `name: Deploy GitPageDocs