@scheduler-systems/gal-run 0.0.377 → 0.0.378

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/dist/index.cjs +148 -6
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3970,7 +3970,7 @@ var cliVersion, defaultApiUrl, BUILD_CONSTANTS, constants_default;
3970
3970
  var init_constants = __esm({
3971
3971
  "src/constants.ts"() {
3972
3972
  "use strict";
3973
- cliVersion = true ? "0.0.377" : "0.0.0-dev";
3973
+ cliVersion = true ? "0.0.378" : "0.0.0-dev";
3974
3974
  defaultApiUrl = true ? "https://api.gal.run" : "http://localhost:3000";
3975
3975
  BUILD_CONSTANTS = Object.freeze([cliVersion, defaultApiUrl]);
3976
3976
  constants_default = BUILD_CONSTANTS;
@@ -4880,7 +4880,7 @@ function detectEnvironment() {
4880
4880
  return "dev";
4881
4881
  }
4882
4882
  try {
4883
- const version2 = true ? "0.0.377" : void 0;
4883
+ const version2 = true ? "0.0.378" : void 0;
4884
4884
  if (version2 && version2.includes("-local")) {
4885
4885
  return "dev";
4886
4886
  }
@@ -5249,7 +5249,7 @@ function getId() {
5249
5249
  }
5250
5250
  function getCliVersion() {
5251
5251
  try {
5252
- return true ? "0.0.377" : "0.0.0-dev";
5252
+ return true ? "0.0.378" : "0.0.0-dev";
5253
5253
  } catch {
5254
5254
  return "0.0.0-dev";
5255
5255
  }
@@ -54040,6 +54040,84 @@ function generateGenericHook(platform5, patterns) {
54040
54040
  }
54041
54041
  };
54042
54042
  }
54043
+ function generateSdlcPrePushHook(apiUrl, orgName) {
54044
+ return `#!/bin/sh
54045
+ # GAL SDLC Phase-Gate Pre-Push Hook
54046
+ # Installed by: gal enforce install --sdlc
54047
+ # Blocks pushes when the SDLC phase gate for the branch is not met.
54048
+ # Fail-open: if the API is unreachable the push is allowed.
54049
+ # To skip: git push --no-verify
54050
+
54051
+ BRANCH=$(git rev-parse --abbrev-ref HEAD)
54052
+
54053
+ # Skip check for main/master \u2014 phase gates apply to feature branches
54054
+ case "$BRANCH" in
54055
+ main|master) exit 0 ;;
54056
+ esac
54057
+
54058
+ API_URL="${apiUrl}"
54059
+ ORG_NAME="${orgName}"
54060
+
54061
+ RESPONSE=$(curl -sf -w "\\n%{http_code}" \\
54062
+ "\${API_URL}/organizations/\${ORG_NAME}/enforcement/sdlc/gate?branch=\${BRANCH}" \\
54063
+ 2>/dev/null) || {
54064
+ # API unreachable \u2014 fail open
54065
+ exit 0
54066
+ }
54067
+
54068
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
54069
+ BODY=$(echo "$RESPONSE" | sed '$ d')
54070
+
54071
+ if [ "$HTTP_CODE" != "200" ]; then
54072
+ # Non-200 \u2014 fail open
54073
+ exit 0
54074
+ fi
54075
+
54076
+ ALLOWED=$(echo "$BODY" | grep -o '"allowed"\\s*:\\s*\\(true\\|false\\)' | grep -o '\\(true\\|false\\)')
54077
+
54078
+ if [ "$ALLOWED" = "false" ]; then
54079
+ MESSAGE=$(echo "$BODY" | grep -o '"message"\\s*:\\s*"[^"]*"' | head -1 | sed 's/"message"\\s*:\\s*"//;s/"$//')
54080
+ echo ""
54081
+ echo "\\033[31m[GAL] SDLC phase gate blocked this push.\\033[0m"
54082
+ echo "\\033[33m Branch: $BRANCH\\033[0m"
54083
+ if [ -n "$MESSAGE" ]; then
54084
+ echo "\\033[33m Reason: $MESSAGE\\033[0m"
54085
+ fi
54086
+ echo "\\033[33m Complete the required SDLC phase before pushing.\\033[0m"
54087
+ echo ""
54088
+ exit 1
54089
+ fi
54090
+
54091
+ exit 0
54092
+ `;
54093
+ }
54094
+ function generateSdlcAgentHook(apiUrl) {
54095
+ const hook = {
54096
+ type: "pre_tool_call",
54097
+ description: "GAL SDLC phase-gate enforcement \u2014 blocks tool calls when the required SDLC phase is not met.",
54098
+ api_url: apiUrl,
54099
+ matchers: [
54100
+ {
54101
+ tool_name: "Bash",
54102
+ command_pattern: "git\\s+push",
54103
+ action: "check_sdlc_gate"
54104
+ },
54105
+ {
54106
+ tool_name: "Edit",
54107
+ file_pattern: "^\\.github/workflows/.*\\.yml$",
54108
+ action: "check_sdlc_gate"
54109
+ }
54110
+ ],
54111
+ check_sdlc_gate: {
54112
+ method: "GET",
54113
+ url: `${apiUrl}/enforcement/sdlc/gate`,
54114
+ query_params: { branch: "{{current_branch}}" },
54115
+ block_when: { allowed: false },
54116
+ fail_open: true
54117
+ }
54118
+ };
54119
+ return JSON.stringify(hook, null, 2);
54120
+ }
54043
54121
  var init_hook_generator = __esm({
54044
54122
  "src/enforcement/hook-generator.ts"() {
54045
54123
  "use strict";
@@ -56667,7 +56745,7 @@ function getEnforcementHookPaths(basePath, platform5) {
56667
56745
  }
56668
56746
  function createEnforceCommand() {
56669
56747
  const enforce = new Command("enforce").description("Install and manage organization security policies locally");
56670
- enforce.command("install").description("Install organization-approved safety hooks from GAL").option("-o, --org <orgName>", "Organization name").option("-p, --platform <platform>", "Specific platform (claude, cursor, gemini, codex, windsurf)").option("--all", "Install hooks for all detected platforms").option("--path <path>", "Base path (default: current directory)").option("--force", "Overwrite existing hooks").option("--dry-run", "Show what would be installed without making changes").action(async (options) => {
56748
+ enforce.command("install").description("Install organization-approved safety hooks from GAL").option("-o, --org <orgName>", "Organization name").option("-p, --platform <platform>", "Specific platform (claude, cursor, gemini, codex, windsurf)").option("--all", "Install hooks for all detected platforms").option("--path <path>", "Base path (default: current directory)").option("--force", "Overwrite existing hooks").option("--dry-run", "Show what would be installed without making changes").option("--sdlc", "Install SDLC phase-gate hooks (git pre-push + Claude Code agent hook)").action(async (options) => {
56671
56749
  const spinner = ora();
56672
56750
  try {
56673
56751
  const config2 = ConfigManager.load();
@@ -56737,6 +56815,29 @@ Installing hooks for: ${platforms.join(", ")}`));
56737
56815
  const msg = err instanceof Error ? err.message : String(err);
56738
56816
  spinner.warn(`Git pre-commit hook: ${msg}`);
56739
56817
  }
56818
+ if (options.sdlc) {
56819
+ const sdlcApiUrl = config2.apiUrl || defaultApiUrl7;
56820
+ spinner.start("Installing SDLC git pre-push hook...");
56821
+ try {
56822
+ if (!options.dryRun) {
56823
+ await installSdlcPrePushHook(basePath, sdlcApiUrl, orgName, options.force);
56824
+ }
56825
+ spinner.succeed(`Installed SDLC git pre-push hook \u2192 ${source_default.dim(".git/hooks/pre-push")}`);
56826
+ } catch (err) {
56827
+ const msg = err instanceof Error ? err.message : String(err);
56828
+ spinner.warn(`SDLC git pre-push hook: ${msg}`);
56829
+ }
56830
+ spinner.start("Installing SDLC Claude Code agent hook...");
56831
+ try {
56832
+ if (!options.dryRun) {
56833
+ await installSdlcAgentHook(basePath, sdlcApiUrl, options.force);
56834
+ }
56835
+ spinner.succeed(`Installed SDLC agent hook \u2192 ${source_default.dim(".claude/hooks/sdlc-phase-gate.json")}`);
56836
+ } catch (err) {
56837
+ const msg = err instanceof Error ? err.message : String(err);
56838
+ spinner.warn(`SDLC agent hook: ${msg}`);
56839
+ }
56840
+ }
56740
56841
  console.log(source_default.green("\nEnforcement hooks installed successfully!"));
56741
56842
  console.log(source_default.dim("AI agents will now enforce your organization's security policies."));
56742
56843
  console.log(source_default.dim("Git commits will be validated against approved policy via pre-commit hook."));
@@ -57673,7 +57774,46 @@ async function removeGitPrecommitHook(basePath) {
57673
57774
  }
57674
57775
  await (0, import_promises4.writeFile)(hookPath, filtered.join("\n"), "utf-8");
57675
57776
  }
57676
- var import_promises4, import_node_fs2, import_node_path3, import_node_crypto, defaultApiUrl7, PLATFORM_DIRS3, SCAN_PLATFORM_DIRS, ENFORCEMENT_HOOK_SUPPORT, GAL_PRECOMMIT_MARKER, GAL_PRECOMMIT_CONTENT;
57777
+ async function installSdlcPrePushHook(basePath, apiUrl, orgName, force) {
57778
+ const gitDir = (0, import_node_path3.join)(basePath, ".git");
57779
+ if (!(0, import_node_fs2.existsSync)(gitDir)) {
57780
+ throw new Error("Not a git repository");
57781
+ }
57782
+ const hooksDir = (0, import_node_path3.join)(gitDir, "hooks");
57783
+ const hookPath = (0, import_node_path3.join)(hooksDir, "pre-push");
57784
+ if ((0, import_node_fs2.existsSync)(hookPath)) {
57785
+ const existing = await (0, import_promises4.readFile)(hookPath, "utf-8");
57786
+ if (existing.includes(GAL_SDLC_PREPUSH_MARKER)) {
57787
+ if (!force) {
57788
+ throw new Error("SDLC pre-push hook already installed. Use --force to overwrite.");
57789
+ }
57790
+ }
57791
+ }
57792
+ await (0, import_promises4.mkdir)(hooksDir, { recursive: true });
57793
+ const hookContent = generateSdlcPrePushHook(apiUrl, orgName);
57794
+ if ((0, import_node_fs2.existsSync)(hookPath)) {
57795
+ const existing = await (0, import_promises4.readFile)(hookPath, "utf-8");
57796
+ if (!existing.includes(GAL_SDLC_PREPUSH_MARKER)) {
57797
+ const appended = existing.trimEnd() + "\n\n" + hookContent.replace("#!/bin/sh\n", "");
57798
+ await (0, import_promises4.writeFile)(hookPath, appended, "utf-8");
57799
+ await (0, import_promises4.chmod)(hookPath, 493);
57800
+ return;
57801
+ }
57802
+ }
57803
+ await (0, import_promises4.writeFile)(hookPath, hookContent, "utf-8");
57804
+ await (0, import_promises4.chmod)(hookPath, 493);
57805
+ }
57806
+ async function installSdlcAgentHook(basePath, apiUrl, force) {
57807
+ const claudeHooksDir = (0, import_node_path3.join)(basePath, ".claude", "hooks");
57808
+ const hookPath = (0, import_node_path3.join)(claudeHooksDir, "sdlc-phase-gate.json");
57809
+ if ((0, import_node_fs2.existsSync)(hookPath) && !force) {
57810
+ throw new Error("SDLC agent hook already installed. Use --force to overwrite.");
57811
+ }
57812
+ await (0, import_promises4.mkdir)(claudeHooksDir, { recursive: true });
57813
+ const hookContent = generateSdlcAgentHook(apiUrl);
57814
+ await (0, import_promises4.writeFile)(hookPath, hookContent, "utf-8");
57815
+ }
57816
+ var import_promises4, import_node_fs2, import_node_path3, import_node_crypto, defaultApiUrl7, PLATFORM_DIRS3, SCAN_PLATFORM_DIRS, ENFORCEMENT_HOOK_SUPPORT, GAL_PRECOMMIT_MARKER, GAL_PRECOMMIT_CONTENT, GAL_SDLC_PREPUSH_MARKER;
57677
57817
  var init_enforce = __esm({
57678
57818
  "src/commands/enforce.ts"() {
57679
57819
  "use strict";
@@ -57685,6 +57825,7 @@ var init_enforce = __esm({
57685
57825
  import_node_path3 = require("node:path");
57686
57826
  import_node_crypto = require("node:crypto");
57687
57827
  init_enforcement();
57828
+ init_hook_generator();
57688
57829
  init_config_manager();
57689
57830
  init_constants();
57690
57831
  init_CoreServiceProvider();
@@ -57742,6 +57883,7 @@ fi
57742
57883
 
57743
57884
  exit $EXIT_CODE
57744
57885
  `;
57886
+ GAL_SDLC_PREPUSH_MARKER = "# GAL SDLC Phase-Gate Pre-Push Hook";
57745
57887
  }
57746
57888
  });
57747
57889
 
@@ -70762,7 +70904,7 @@ var init_index = __esm({
70762
70904
  });
70763
70905
 
70764
70906
  // src/bootstrap.ts
70765
- var cliVersion10 = true ? "0.0.377" : "0.0.0-dev";
70907
+ var cliVersion10 = true ? "0.0.378" : "0.0.0-dev";
70766
70908
  var args = process.argv.slice(2);
70767
70909
  var requestedGlobalHelp = args.length === 1 && (args[0] === "--help" || args[0] === "-h");
70768
70910
  var requestedVersion = args.length === 1 && (args[0] === "--version" || args[0] === "-V");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scheduler-systems/gal-run",
3
- "version": "0.0.377",
3
+ "version": "0.0.378",
4
4
  "description": "GAL CLI - Command-line tool for managing AI agent configurations across your organization",
5
5
  "license": "Elastic-2.0",
6
6
  "private": false,