javi-forge 1.9.1 → 1.10.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.
@@ -11,7 +11,49 @@ import { render } from "ink";
11
11
  import React from "react";
12
12
  import CI from "../../ui/CI.js";
13
13
  import { CIProvider as CIContextProvider } from "../../ui/CIContext.js";
14
+ import { CI_HELP_TEXT } from "../help.js";
14
15
  export async function handleCi(cli, ctx) {
16
+ // Per-command help: `javi-forge ci --help` shows ci-specific usage, not the
17
+ // global banner (autoHelp is disabled at the entrypoint).
18
+ if (cli.flags.help === true) {
19
+ console.log(CI_HELP_TEXT);
20
+ process.exit(0);
21
+ }
22
+ // Sub-command: javi-forge ci validate → dry config validation, no execution.
23
+ if (cli.input[1] === "validate") {
24
+ const { validateCIConfig } = await import("../../commands/ci-validate.js");
25
+ const result = await validateCIConfig(process.cwd(), cli.flags.config || undefined);
26
+ if (result.ok) {
27
+ if (result.mode === "auto-detect") {
28
+ if (cli.flags.json) {
29
+ console.log(JSON.stringify({ ok: true, mode: "auto-detect", runners: [] }, null, 2));
30
+ }
31
+ else {
32
+ console.log("✓ No .javi-forge/ci.yaml — auto-detect mode (no config to validate)");
33
+ }
34
+ }
35
+ else if (cli.flags.json) {
36
+ console.log(JSON.stringify({ ok: true, runners: result.runners }, null, 2));
37
+ }
38
+ else {
39
+ console.log(`✓ CI config valid: ${result.configPath}`);
40
+ console.log(` ${result.runners.length} runner(s):`);
41
+ for (const runner of result.runners) {
42
+ console.log(` - ${runner.name} (${runner.stack})`);
43
+ }
44
+ }
45
+ process.exit(0);
46
+ }
47
+ if (cli.flags.json) {
48
+ console.log(JSON.stringify({ ok: false, errors: result.errors }, null, 2));
49
+ }
50
+ else {
51
+ for (const err of result.errors) {
52
+ console.error(`${err.path}: ${err.message}`);
53
+ }
54
+ }
55
+ process.exit(1);
56
+ }
15
57
  // Sub-command: javi-forge ci init → install git hooks
16
58
  if (cli.input[1] === "init") {
17
59
  const { installCIHooks } = await import("../../commands/ci.js");
@@ -36,6 +78,12 @@ export async function handleCi(cli, ctx) {
36
78
  }
37
79
  process.exit(errors.length > 0 ? 1 : 0);
38
80
  }
81
+ // Any other positional is an unknown subcommand — show ci usage, don't run
82
+ // the pipeline against a typo.
83
+ if (cli.input[1] !== undefined) {
84
+ console.log(CI_HELP_TEXT);
85
+ process.exit(1);
86
+ }
39
87
  const ciMode = cli.flags.detect
40
88
  ? "detect"
41
89
  : cli.flags.shell
@@ -8,8 +8,19 @@
8
8
  * Help banner shown by meow when `--help` is passed or invalid args are supplied.
9
9
  * Multi-line template literal — preserve exact formatting (whitespace is significant).
10
10
  */
11
- export declare const HELP_TEXT = "\n Usage\n $ javi-forge [command] [options]\n\n Commands\n init Bootstrap a new project (default)\n ci Run CI simulation (lint + compile + test + security + ghagga)\n tdd init Install TDD-enforcing pre-commit hook (auto-detects stack)\n tdd pipeline Install TDD pipeline pre-push hook (--mode strict|warn)\n analyze Run repoforge skills analysis\n doctor Show health report\n workflow show Render a workflow graph as ASCII (--template <name> or file path)\n workflow validate Validate project state against a workflow graph\n workflow list List available workflows and built-in templates\n plugin add Install a plugin from GitHub (org/repo)\n plugin remove Remove an installed plugin\n plugin list List installed plugins\n plugin search Search the plugin registry\n plugin validate Validate a local plugin directory\n plugin sync Auto-detect and wire installed plugins\n plugin export Export plugin to Agent Skills spec format (skills.json)\n plugin export --codex: Export plugin to Codex-compatible TOML subagent files\n plugin export-skills Generate aggregated skills.json from all installed plugins\n plugin export-skills global Generate global skills.json from all globally installed plugins\n plugin import Import an Agent Skills spec package as a javi-forge plugin\n skills doctor Show skills health report (add --deep for conflict detection)\n skills budget Show token cost of loaded skills (add -b N for custom budget)\n skills score Score a skill on quality dimensions (completeness, clarity, testability, token-efficiency)\n skills benchmark Benchmark a skill with structural quality checks\n skills auto Auto-detect project stack and suggest/install matching AI skills\n skills auto-install Alias for skills auto\n skill publish Package a skill directory for marketplace distribution (generates plugin.json)\n security baseline Create security baseline from current audit findings\n security check Check for regressions against baseline (exits non-zero if found)\n security update Re-snapshot baseline (acknowledge current vulns)\n security allowlist Add all current findings to the allowlist (suppress in future checks)\n llms-txt Generate AI-friendly llms.txt for current project\n\n Options\n --dry-run Preview changes without writing files\n --stack Project stack (node, python, go, rust, java-gradle, java-maven, elixir)\n --ci CI provider (github, gitlab, woodpecker)\n --memory Memory module (engram, obsidian-brain, memory-simple, none)\n --project-name Project name (skips name prompt)\n --ghagga Enable GHAGGA review system\n --mock Enable mock-first mode (no real API keys needed)\n --local-ai Include local AI dev stack (Ollama + Docker Compose)\n --batch Non-interactive mode (auto-proceed, no keyboard input)\n --deep Enable deep analysis (conflict + duplicate detection)\n --budget, -b Token budget limit for skills (default: 8000)\n --skills-dir Custom skills directory path\n --author Author name for skill publish\n --repo Repository URL for skill publish\n --version Show version\n --help Show this help\n\n CI options (javi-forge ci)\n --quick Lint + compile only (fast, for pre-commit)\n --shell Open interactive shell in CI container\n --detect Show detected stack and exit\n --config PATH Load ordered CI runners from a versioned config file\n (default discovery: .javi-forge/ci.yaml)\n --stack STACK Force a single explicit stack (single-stack repos only \u2014\n insufficient for hybrid repos; use --config instead)\n --no-docker Run commands natively (no Docker)\n --no-ci-ghagga Skip GHAGGA review\n --no-security Skip Semgrep security scan\n --timeout N Per-step timeout in seconds (default: 600)\n\n CI hooks (javi-forge ci init)\n Install git hooks that call javi-forge ci.\n No files copied \u2014 hooks reference the global CLI.\n Existing hooks javi-forge did not write are refused, never clobbered.\n --force Overwrite a foreign or locally modified hook. The previous\n content is copied to a .bak sibling first; if that backup\n cannot be written, the hook is left untouched. Symlinked\n hook paths are refused even with --force.\n\n Examples\n $ javi-forge\n $ javi-forge init --dry-run\n $ javi-forge init --stack node --ci github\n $ javi-forge ci\n $ javi-forge ci init\n $ javi-forge ci init --force\n $ javi-forge tdd init\n $ javi-forge ci --quick\n $ javi-forge ci --no-ci-ghagga --no-security\n $ javi-forge ci --no-docker\n $ javi-forge ci --shell\n $ javi-forge ci --config .javi-forge/ci.yaml\n $ javi-forge analyze\n $ javi-forge doctor\n $ javi-forge plugin add mapbox/agent-skills\n $ javi-forge plugin list\n";
11
+ export declare const HELP_TEXT = "\n Usage\n $ javi-forge [command] [options]\n\n Commands\n init Bootstrap a new project (default)\n ci Run CI simulation (lint + compile + test + security + ghagga)\n ci validate Validate .javi-forge/ci.yaml without running anything\n ci init Install git hooks that call javi-forge ci\n tdd init Install TDD-enforcing pre-commit hook (auto-detects stack)\n tdd pipeline Install TDD pipeline pre-push hook (--mode strict|warn)\n analyze Run repoforge skills analysis\n doctor Show health report\n workflow show Render a workflow graph as ASCII (--template <name> or file path)\n workflow validate Validate project state against a workflow graph\n workflow list List available workflows and built-in templates\n plugin add Install a plugin from GitHub (org/repo)\n plugin remove Remove an installed plugin\n plugin list List installed plugins\n plugin search Search the plugin registry\n plugin validate Validate a local plugin directory\n plugin sync Auto-detect and wire installed plugins\n plugin export Export plugin to Agent Skills spec format (skills.json)\n plugin export --codex: Export plugin to Codex-compatible TOML subagent files\n plugin export-skills Generate aggregated skills.json from all installed plugins\n plugin export-skills global Generate global skills.json from all globally installed plugins\n plugin import Import an Agent Skills spec package as a javi-forge plugin\n skills doctor Show skills health report (add --deep for conflict detection)\n skills budget Show token cost of loaded skills (add -b N for custom budget)\n skills score Score a skill on quality dimensions (completeness, clarity, testability, token-efficiency)\n skills benchmark Benchmark a skill with structural quality checks\n skills auto Auto-detect project stack and suggest/install matching AI skills\n skills auto-install Alias for skills auto\n skill publish Package a skill directory for marketplace distribution (generates plugin.json)\n security baseline Create security baseline from current audit findings\n security check Check for regressions against baseline (exits non-zero if found)\n security update Re-snapshot baseline (acknowledge current vulns)\n security allowlist Add all current findings to the allowlist (suppress in future checks)\n llms-txt Generate AI-friendly llms.txt for current project\n\n Options\n --dry-run Preview changes without writing files\n --stack Project stack (node, python, go, rust, java-gradle, java-maven, elixir)\n --ci CI provider (github, gitlab, woodpecker)\n --memory Memory module (engram, obsidian-brain, memory-simple, none)\n --project-name Project name (skips name prompt)\n --ghagga Enable GHAGGA review system\n --mock Enable mock-first mode (no real API keys needed)\n --local-ai Include local AI dev stack (Ollama + Docker Compose)\n --batch Non-interactive mode (auto-proceed, no keyboard input)\n --deep Enable deep analysis (conflict + duplicate detection)\n --budget, -b Token budget limit for skills (default: 8000)\n --skills-dir Custom skills directory path\n --author Author name for skill publish\n --repo Repository URL for skill publish\n --version Show version\n --help Show this help\n\n CI options (javi-forge ci)\n --quick Lint + compile only (fast, for pre-commit)\n --shell Open interactive shell in CI container\n --detect Show detected stack and exit\n --config PATH Load ordered CI runners from a versioned config file\n (default discovery: .javi-forge/ci.yaml)\n --stack STACK Force a single explicit stack (single-stack repos only \u2014\n insufficient for hybrid repos; use --config instead)\n --no-docker Run commands natively (no Docker)\n --no-ci-ghagga Skip GHAGGA review\n --no-security Skip Semgrep security scan\n --timeout N Per-step timeout in seconds (default: 600)\n\n CI hooks (javi-forge ci init)\n Install git hooks that call javi-forge ci.\n No files copied \u2014 hooks reference the global CLI.\n Existing hooks javi-forge did not write are refused, never clobbered.\n --force Overwrite a foreign or locally modified hook. The previous\n content is copied to a .bak sibling first; if that backup\n cannot be written, the hook is left untouched. Symlinked\n hook paths are refused even with --force.\n\n Examples\n $ javi-forge\n $ javi-forge init --dry-run\n $ javi-forge init --stack node --ci github\n $ javi-forge ci\n $ javi-forge ci init\n $ javi-forge ci init --force\n $ javi-forge tdd init\n $ javi-forge ci --quick\n $ javi-forge ci --no-ci-ghagga --no-security\n $ javi-forge ci --no-docker\n $ javi-forge ci --shell\n $ javi-forge ci --config .javi-forge/ci.yaml\n $ javi-forge ci validate\n $ javi-forge ci --help\n $ javi-forge analyze\n $ javi-forge doctor\n $ javi-forge plugin add mapbox/agent-skills\n $ javi-forge plugin list\n";
12
+ /**
13
+ * Per-command help for `ci`, shown by `javi-forge ci --help` (or when `ci` is
14
+ * given an unknown subcommand). Kept consistent with the global HELP_TEXT
15
+ * layout — whitespace is significant.
16
+ */
17
+ export declare const CI_HELP_TEXT = "\n Usage\n $ javi-forge ci [subcommand] [options]\n\n Run a local CI simulation (lint + compile + test + security + ghagga).\n With no subcommand, the full pipeline runs.\n\n Subcommands\n init Install git hooks that call javi-forge ci\n validate Validate .javi-forge/ci.yaml without running anything\n\n Options\n --quick Lint + compile only (fast, for pre-commit)\n --no-docker Run commands natively (no Docker)\n --no-security Skip Semgrep security scan\n --no-ci-ghagga Skip GHAGGA review\n --force (ci init) Overwrite a foreign or modified hook (backs up first)\n --config PATH Load ordered CI runners from a versioned config file\n (default discovery: .javi-forge/ci.yaml)\n --stack STACK Force a single explicit stack (single-stack repos only)\n --json (ci validate) Emit the result as JSON\n --help Show this help\n\n Examples\n $ javi-forge ci\n $ javi-forge ci --quick\n $ javi-forge ci validate\n $ javi-forge ci validate --json\n $ javi-forge ci init --force\n";
12
18
  export declare const FLAGS_SCHEMA: {
19
+ readonly help: {
20
+ readonly type: "boolean";
21
+ readonly shortFlag: "h";
22
+ readonly default: false;
23
+ };
13
24
  readonly dryRun: {
14
25
  readonly type: "boolean";
15
26
  readonly default: false;
package/dist/cli/help.js CHANGED
@@ -15,6 +15,8 @@ export const HELP_TEXT = `
15
15
  Commands
16
16
  init Bootstrap a new project (default)
17
17
  ci Run CI simulation (lint + compile + test + security + ghagga)
18
+ ci validate Validate .javi-forge/ci.yaml without running anything
19
+ ci init Install git hooks that call javi-forge ci
18
20
  tdd init Install TDD-enforcing pre-commit hook (auto-detects stack)
19
21
  tdd pipeline Install TDD pipeline pre-push hook (--mode strict|warn)
20
22
  analyze Run repoforge skills analysis
@@ -99,12 +101,52 @@ export const HELP_TEXT = `
99
101
  $ javi-forge ci --no-docker
100
102
  $ javi-forge ci --shell
101
103
  $ javi-forge ci --config .javi-forge/ci.yaml
104
+ $ javi-forge ci validate
105
+ $ javi-forge ci --help
102
106
  $ javi-forge analyze
103
107
  $ javi-forge doctor
104
108
  $ javi-forge plugin add mapbox/agent-skills
105
109
  $ javi-forge plugin list
106
110
  `;
111
+ /**
112
+ * Per-command help for `ci`, shown by `javi-forge ci --help` (or when `ci` is
113
+ * given an unknown subcommand). Kept consistent with the global HELP_TEXT
114
+ * layout — whitespace is significant.
115
+ */
116
+ export const CI_HELP_TEXT = `
117
+ Usage
118
+ $ javi-forge ci [subcommand] [options]
119
+
120
+ Run a local CI simulation (lint + compile + test + security + ghagga).
121
+ With no subcommand, the full pipeline runs.
122
+
123
+ Subcommands
124
+ init Install git hooks that call javi-forge ci
125
+ validate Validate .javi-forge/ci.yaml without running anything
126
+
127
+ Options
128
+ --quick Lint + compile only (fast, for pre-commit)
129
+ --no-docker Run commands natively (no Docker)
130
+ --no-security Skip Semgrep security scan
131
+ --no-ci-ghagga Skip GHAGGA review
132
+ --force (ci init) Overwrite a foreign or modified hook (backs up first)
133
+ --config PATH Load ordered CI runners from a versioned config file
134
+ (default discovery: .javi-forge/ci.yaml)
135
+ --stack STACK Force a single explicit stack (single-stack repos only)
136
+ --json (ci validate) Emit the result as JSON
137
+ --help Show this help
138
+
139
+ Examples
140
+ $ javi-forge ci
141
+ $ javi-forge ci --quick
142
+ $ javi-forge ci validate
143
+ $ javi-forge ci validate --json
144
+ $ javi-forge ci init --force
145
+ `;
107
146
  export const FLAGS_SCHEMA = {
147
+ // `--help` is handled manually (autoHelp is disabled at the entrypoint so
148
+ // `ci --help` can show ci-specific usage instead of the global banner).
149
+ help: { type: "boolean", shortFlag: "h", default: false },
108
150
  dryRun: { type: "boolean", default: false },
109
151
  stack: { type: "string", default: "" },
110
152
  ci: { type: "string", default: "" },
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `javi-forge ci validate` — dry validation of `.javi-forge/ci.yaml`.
3
+ *
4
+ * Pure parse-and-report over the existing validator (src/lib/ci-config.ts):
5
+ * resolve the config path (same discovery `runCI` uses), call `loadCIConfig`,
6
+ * and return a structured result. It NEVER builds images, runs Docker, or
7
+ * executes any phase — the CLI surface stops BEFORE execution.
8
+ */
9
+ import { type CIConfigValidationError } from "../lib/ci-config.js";
10
+ /** One validated runner, reduced to what the report shows. */
11
+ export interface CIValidateRunnerSummary {
12
+ name: string;
13
+ stack: string;
14
+ }
15
+ export interface CIValidateOk {
16
+ ok: true;
17
+ /**
18
+ * "config" — a config file was found and validated.
19
+ * "auto-detect" — no config exists and none was explicitly requested, so
20
+ * `runCI` would use its zero-config single-runner auto-detect path
21
+ * (ci.ts:396-406). There is nothing to validate, and that is valid.
22
+ */
23
+ mode: "config" | "auto-detect";
24
+ /** Resolved config path, or null in auto-detect mode. */
25
+ configPath: string | null;
26
+ runners: CIValidateRunnerSummary[];
27
+ }
28
+ export interface CIValidateErr {
29
+ ok: false;
30
+ /** Resolved path we looked at, or null when discovery found nothing. */
31
+ configPath: string | null;
32
+ errors: CIConfigValidationError[];
33
+ }
34
+ export type CIValidateResult = CIValidateOk | CIValidateErr;
35
+ /**
36
+ * Validate a project's CI config without executing anything.
37
+ *
38
+ * @param projectDir Project root to discover `.javi-forge/ci.yaml` in.
39
+ * @param config Optional explicit config path (from `--config`).
40
+ */
41
+ export declare function validateCIConfig(projectDir: string, config?: string): Promise<CIValidateResult>;
42
+ //# sourceMappingURL=ci-validate.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `javi-forge ci validate` — dry validation of `.javi-forge/ci.yaml`.
3
+ *
4
+ * Pure parse-and-report over the existing validator (src/lib/ci-config.ts):
5
+ * resolve the config path (same discovery `runCI` uses), call `loadCIConfig`,
6
+ * and return a structured result. It NEVER builds images, runs Docker, or
7
+ * executes any phase — the CLI surface stops BEFORE execution.
8
+ */
9
+ import fs from "fs-extra";
10
+ import { CIConfigError, findCIConfig, loadCIConfig, } from "../lib/ci-config.js";
11
+ /**
12
+ * Validate a project's CI config without executing anything.
13
+ *
14
+ * @param projectDir Project root to discover `.javi-forge/ci.yaml` in.
15
+ * @param config Optional explicit config path (from `--config`).
16
+ */
17
+ export async function validateCIConfig(projectDir, config) {
18
+ // Discriminator: was a config path explicitly requested (`--config`)?
19
+ // - explicit path that is missing → ERROR (the user asserted a file that
20
+ // isn't there).
21
+ // - discovery finds nothing → VALID auto-detect: `runCI` runs fine in this
22
+ // exact state via its zero-config single-runner path (ci.ts:396-406), so
23
+ // there is nothing to validate and that is not a failure.
24
+ let configPath;
25
+ if (config) {
26
+ if (!(await fs.pathExists(config))) {
27
+ return {
28
+ ok: false,
29
+ configPath: config,
30
+ errors: [{ path: config, message: `no CI config found at ${config}` }],
31
+ };
32
+ }
33
+ configPath = config;
34
+ }
35
+ else {
36
+ const discovered = await findCIConfig(projectDir);
37
+ if (!discovered) {
38
+ return { ok: true, mode: "auto-detect", configPath: null, runners: [] };
39
+ }
40
+ configPath = discovered;
41
+ }
42
+ try {
43
+ const ciConfig = await loadCIConfig(configPath);
44
+ return {
45
+ ok: true,
46
+ mode: "config",
47
+ configPath,
48
+ runners: ciConfig.runners.map((r) => ({
49
+ name: r.name,
50
+ stack: r.stack,
51
+ })),
52
+ };
53
+ }
54
+ catch (e) {
55
+ if (e instanceof CIConfigError) {
56
+ return { ok: false, configPath, errors: e.errors };
57
+ }
58
+ throw e;
59
+ }
60
+ }
61
+ //# sourceMappingURL=ci-validate.js.map
package/dist/index.js CHANGED
@@ -17,8 +17,17 @@ setupUpdateNotifier(pkg);
17
17
  const cli = meow(HELP_TEXT, {
18
18
  importMeta: import.meta,
19
19
  flags: FLAGS_SCHEMA,
20
+ // Handle --help manually so `ci --help` can show ci-specific usage instead
21
+ // of the global banner (meow's autoHelp would print + exit before dispatch).
22
+ autoHelp: false,
20
23
  });
21
24
  const subcommand = cli.input[0] ?? "init";
25
+ // Global --help: every command except `ci` shows the global banner here. `ci`
26
+ // owns its per-command help inside handleCi.
27
+ if (cli.flags.help && subcommand !== "ci") {
28
+ console.log(HELP_TEXT);
29
+ process.exit(0);
30
+ }
22
31
  const isCI = detectCI(cli.flags);
23
32
  const inkStdin = createInkStdin();
24
33
  switch (subcommand) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javi-forge",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "Project scaffolding and AI-ready CI bootstrap",
5
5
  "type": "module",
6
6
  "bin": {