// src/index.ts
import { Command as Command4 } from "commander";
import { VERSION } from "@harness-engineering/core";

// src/commands/validate.ts
import { Command } from "commander";
import * as path2 from "path";
import { Ok as Ok2 } from "@harness-engineering/core";
import { validateAgentsMap, validateKnowledgeMap } from "@harness-engineering/core";

// src/config/loader.ts
import * as fs from "fs";
import * as path from "path";
import { Ok, Err } from "@harness-engineering/core";

// src/config/schema.ts
import { z } from "zod";
var LayerSchema = z.object({
  name: z.string(),
  pattern: z.string(),
  allowedDependencies: z.array(z.string())
});
var ForbiddenImportSchema = z.object({
  from: z.string(),
  disallow: z.array(z.string()),
  message: z.string().optional()
});
var BoundaryConfigSchema = z.object({
  requireSchema: z.array(z.string())
});
var AgentConfigSchema = z.object({
  executor: z.enum(["subprocess", "cloud", "noop"]).default("subprocess"),
  timeout: z.number().default(3e5),
  skills: z.array(z.string()).optional()
});
var EntropyConfigSchema = z.object({
  excludePatterns: z.array(z.string()).default(["**/node_modules/**", "**/*.test.ts"]),
  autoFix: z.boolean().default(false)
});
var HarnessConfigSchema = z.object({
  version: z.literal(1),
  name: z.string().optional(),
  rootDir: z.string().default("."),
  layers: z.array(LayerSchema).optional(),
  forbiddenImports: z.array(ForbiddenImportSchema).optional(),
  boundaries: BoundaryConfigSchema.optional(),
  agentsMapPath: z.string().default("./AGENTS.md"),
  docsDir: z.string().default("./docs"),
  agent: AgentConfigSchema.optional(),
  entropy: EntropyConfigSchema.optional()
});

// src/utils/errors.ts
var ExitCode = {
  SUCCESS: 0,
  VALIDATION_FAILED: 1,
  ERROR: 2
};
var CLIError = class extends Error {
  exitCode;
  constructor(message, exitCode = ExitCode.ERROR) {
    super(message);
    this.name = "CLIError";
    this.exitCode = exitCode;
  }
};
function formatError(error) {
  if (error instanceof CLIError) {
    return `Error: ${error.message}`;
  }
  if (error instanceof Error) {
    return `Error: ${error.message}`;
  }
  return `Error: ${String(error)}`;
}
function handleError(error) {
  const message = formatError(error);
  console.error(message);
  const exitCode = error instanceof CLIError ? error.exitCode : ExitCode.ERROR;
  process.exit(exitCode);
}

// src/config/loader.ts
var CONFIG_FILENAMES = ["harness.config.json"];
function findConfigFile(startDir = process.cwd()) {
  let currentDir = path.resolve(startDir);
  const root = path.parse(currentDir).root;
  while (currentDir !== root) {
    for (const filename of CONFIG_FILENAMES) {
      const configPath = path.join(currentDir, filename);
      if (fs.existsSync(configPath)) {
        return Ok(configPath);
      }
    }
    currentDir = path.dirname(currentDir);
  }
  return Err(new CLIError(
    'No harness.config.json found. Run "harness init" to create one.',
    ExitCode.ERROR
  ));
}
function loadConfig(configPath) {
  if (!fs.existsSync(configPath)) {
    return Err(new CLIError(
      `Config file not found: ${configPath}`,
      ExitCode.ERROR
    ));
  }
  let rawConfig;
  try {
    const content = fs.readFileSync(configPath, "utf-8");
    rawConfig = JSON.parse(content);
  } catch (error) {
    return Err(new CLIError(
      `Failed to parse config: ${error instanceof Error ? error.message : "Unknown error"}`,
      ExitCode.ERROR
    ));
  }
  const parsed = HarnessConfigSchema.safeParse(rawConfig);
  if (!parsed.success) {
    const issues = parsed.error.issues.map((i) => `  - ${i.path.join(".")}: ${i.message}`).join("\n");
    return Err(new CLIError(
      `Invalid config:
${issues}`,
      ExitCode.ERROR
    ));
  }
  return Ok(parsed.data);
}
function resolveConfig(configPath) {
  if (configPath) {
    return loadConfig(configPath);
  }
  const findResult = findConfigFile();
  if (!findResult.ok) {
    return findResult;
  }
  return loadConfig(findResult.value);
}

// src/output/formatter.ts
import chalk from "chalk";
var OutputMode = {
  JSON: "json",
  TEXT: "text",
  QUIET: "quiet",
  VERBOSE: "verbose"
};
var OutputFormatter = class {
  constructor(mode = OutputMode.TEXT) {
    this.mode = mode;
  }
  /**
   * Format raw data (for JSON mode)
   */
  format(data) {
    if (this.mode === OutputMode.JSON) {
      return JSON.stringify(data, null, 2);
    }
    return String(data);
  }
  /**
   * Format validation result
   */
  formatValidation(result) {
    if (this.mode === OutputMode.JSON) {
      return JSON.stringify(result, null, 2);
    }
    if (this.mode === OutputMode.QUIET) {
      if (result.valid) return "";
      return result.issues.map((i) => `${i.file ?? ""}: ${i.message}`).join("\n");
    }
    const lines = [];
    if (result.valid) {
      lines.push(chalk.green("v validation passed"));
    } else {
      lines.push(chalk.red(`x Validation failed (${result.issues.length} issues)`));
      lines.push("");
      for (const issue of result.issues) {
        const location = issue.file ? issue.line ? `${issue.file}:${issue.line}` : issue.file : "unknown";
        lines.push(`  ${chalk.yellow("*")} ${chalk.dim(location)}`);
        lines.push(`    ${issue.message}`);
        if (issue.suggestion && this.mode === OutputMode.VERBOSE) {
          lines.push(`    ${chalk.dim("->")} ${issue.suggestion}`);
        }
      }
    }
    return lines.join("\n");
  }
  /**
   * Format a summary line
   */
  formatSummary(label, value, success) {
    if (this.mode === OutputMode.JSON || this.mode === OutputMode.QUIET) {
      return "";
    }
    const icon = success ? chalk.green("v") : chalk.red("x");
    return `${icon} ${label}: ${value}`;
  }
};

// src/output/logger.ts
import chalk2 from "chalk";
var logger = {
  info: (message) => console.log(chalk2.blue("i"), message),
  success: (message) => console.log(chalk2.green("v"), message),
  warn: (message) => console.log(chalk2.yellow("!"), message),
  error: (message) => console.error(chalk2.red("x"), message),
  dim: (message) => console.log(chalk2.dim(message)),
  // For JSON output mode
  raw: (data) => console.log(JSON.stringify(data, null, 2))
};

// src/commands/validate.ts
async function runValidate(options) {
  const configResult = resolveConfig(options.configPath);
  if (!configResult.ok) {
    return configResult;
  }
  const config = configResult.value;
  const cwd = options.cwd ?? (options.configPath ? path2.dirname(path2.resolve(options.configPath)) : process.cwd());
  const result = {
    valid: true,
    checks: {
      agentsMap: false,
      fileStructure: false,
      knowledgeMap: false
    },
    issues: []
  };
  const agentsMapPath = path2.resolve(cwd, config.agentsMapPath);
  const agentsResult = await validateAgentsMap(agentsMapPath);
  if (agentsResult.ok) {
    result.checks.agentsMap = true;
  } else {
    result.valid = false;
    result.issues.push({
      check: "agentsMap",
      file: config.agentsMapPath,
      message: agentsResult.error.message,
      suggestion: agentsResult.error.suggestions?.[0]
    });
  }
  const knowledgeResult = await validateKnowledgeMap(cwd);
  if (knowledgeResult.ok && knowledgeResult.value.brokenLinks.length === 0) {
    result.checks.knowledgeMap = true;
  } else if (knowledgeResult.ok) {
    result.valid = false;
    for (const broken of knowledgeResult.value.brokenLinks) {
      result.issues.push({
        check: "knowledgeMap",
        file: broken.path,
        message: `Broken link: ${broken.path}`,
        suggestion: broken.suggestion || "Remove or fix the broken link"
      });
    }
  } else {
    result.valid = false;
    result.issues.push({
      check: "knowledgeMap",
      message: knowledgeResult.error.message
    });
  }
  result.checks.fileStructure = true;
  return Ok2(result);
}
function createValidateCommand() {
  const command = new Command("validate").description("Run all validation checks").action(async (opts, cmd) => {
    const globalOpts = cmd.optsWithGlobals();
    const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
    const formatter = new OutputFormatter(mode);
    const result = await runValidate({
      configPath: globalOpts.config,
      json: globalOpts.json,
      verbose: globalOpts.verbose,
      quiet: globalOpts.quiet
    });
    if (!result.ok) {
      if (mode === OutputMode.JSON) {
        console.log(JSON.stringify({ error: result.error.message }));
      } else {
        logger.error(result.error.message);
      }
      process.exit(result.error.exitCode);
    }
    const output = formatter.formatValidation({
      valid: result.value.valid,
      issues: result.value.issues
    });
    if (output) {
      console.log(output);
    }
    process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
  });
  return command;
}

// src/commands/check-deps.ts
import { Command as Command2 } from "commander";
import * as path3 from "path";
import { Ok as Ok3 } from "@harness-engineering/core";
import {
  validateDependencies,
  detectCircularDepsInFiles,
  defineLayer,
  TypeScriptParser
} from "@harness-engineering/core";

// src/utils/files.ts
import { glob } from "glob";
async function findFiles(pattern, cwd = process.cwd()) {
  return glob(pattern, { cwd, absolute: true });
}

// src/commands/check-deps.ts
async function runCheckDeps(options) {
  const cwd = options.cwd ?? process.cwd();
  const configResult = resolveConfig(options.configPath);
  if (!configResult.ok) {
    return configResult;
  }
  const config = configResult.value;
  const result = {
    valid: true,
    layerViolations: [],
    circularDeps: []
  };
  if (!config.layers || config.layers.length === 0) {
    return Ok3(result);
  }
  const rootDir = path3.resolve(cwd, config.rootDir);
  const parser = new TypeScriptParser();
  const layers = config.layers.map(
    (l) => defineLayer(l.name, [l.pattern], l.allowedDependencies)
  );
  const layerConfig = {
    layers,
    rootDir,
    parser,
    fallbackBehavior: "warn"
  };
  const depsResult = await validateDependencies(layerConfig);
  if (depsResult.ok) {
    for (const violation of depsResult.value.violations) {
      result.valid = false;
      result.layerViolations.push({
        file: violation.file,
        imports: violation.imports,
        fromLayer: violation.fromLayer ?? "unknown",
        toLayer: violation.toLayer ?? "unknown",
        message: violation.reason
      });
    }
  }
  const allFiles = [];
  for (const layer of config.layers) {
    const files = await findFiles(layer.pattern, rootDir);
    allFiles.push(...files);
  }
  const uniqueFiles = [...new Set(allFiles)];
  if (uniqueFiles.length > 0) {
    const circularResult = await detectCircularDepsInFiles(uniqueFiles, parser);
    if (circularResult.ok && circularResult.value.hasCycles) {
      result.valid = false;
      for (const cycle of circularResult.value.cycles) {
        result.circularDeps.push({ cycle: cycle.cycle });
      }
    }
  }
  return Ok3(result);
}
function createCheckDepsCommand() {
  const command = new Command2("check-deps").description("Validate dependency layers and detect circular dependencies").action(async (_opts, cmd) => {
    const globalOpts = cmd.optsWithGlobals();
    const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
    const formatter = new OutputFormatter(mode);
    const result = await runCheckDeps({
      configPath: globalOpts.config,
      json: globalOpts.json,
      verbose: globalOpts.verbose,
      quiet: globalOpts.quiet
    });
    if (!result.ok) {
      if (mode === OutputMode.JSON) {
        console.log(JSON.stringify({ error: result.error.message }));
      } else {
        logger.error(result.error.message);
      }
      process.exit(result.error.exitCode);
    }
    const issues = [
      ...result.value.layerViolations.map((v) => ({
        file: v.file,
        message: `Layer violation: ${v.fromLayer} -> ${v.toLayer} (${v.message})`
      })),
      ...result.value.circularDeps.map((c) => ({
        message: `Circular dependency: ${c.cycle.join(" -> ")}`
      }))
    ];
    const output = formatter.formatValidation({
      valid: result.value.valid,
      issues
    });
    if (output) {
      console.log(output);
    }
    process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
  });
  return command;
}

// src/commands/check-docs.ts
import { Command as Command3 } from "commander";
import * as path4 from "path";
import { Ok as Ok4, Err as Err3 } from "@harness-engineering/core";
import { checkDocCoverage, validateKnowledgeMap as validateKnowledgeMap2 } from "@harness-engineering/core";
async function runCheckDocs(options) {
  const cwd = options.cwd ?? process.cwd();
  const minCoverage = options.minCoverage ?? 80;
  const configResult = resolveConfig(options.configPath);
  if (!configResult.ok) {
    return configResult;
  }
  const config = configResult.value;
  const docsDir = path4.resolve(cwd, config.docsDir);
  const sourceDir = path4.resolve(cwd, config.rootDir);
  const coverageResult = await checkDocCoverage("project", {
    docsDir,
    sourceDir,
    excludePatterns: ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"]
  });
  if (!coverageResult.ok) {
    return Err3(new CLIError(
      `Documentation coverage check failed: ${coverageResult.error.message}`,
      ExitCode.ERROR
    ));
  }
  const knowledgeResult = await validateKnowledgeMap2(cwd);
  let brokenLinks = [];
  if (knowledgeResult.ok) {
    brokenLinks = knowledgeResult.value.brokenLinks.map((b) => b.path);
  } else {
    logger.warn(`Knowledge map validation failed: ${knowledgeResult.error.message}`);
  }
  const coveragePercent = coverageResult.value.coveragePercentage;
  const result = {
    valid: coveragePercent >= minCoverage && brokenLinks.length === 0,
    coveragePercent,
    documented: coverageResult.value.documented,
    undocumented: coverageResult.value.undocumented,
    brokenLinks
  };
  return Ok4(result);
}
function createCheckDocsCommand() {
  const command = new Command3("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
    const globalOpts = cmd.optsWithGlobals();
    const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
    const formatter = new OutputFormatter(mode);
    const result = await runCheckDocs({
      configPath: globalOpts.config,
      minCoverage: parseInt(opts.minCoverage, 10),
      json: globalOpts.json,
      verbose: globalOpts.verbose,
      quiet: globalOpts.quiet
    });
    if (!result.ok) {
      if (mode === OutputMode.JSON) {
        console.log(JSON.stringify({ error: result.error.message }));
      } else {
        logger.error(result.error.message);
      }
      process.exit(result.error.exitCode);
    }
    if (mode === OutputMode.JSON) {
      console.log(JSON.stringify(result.value, null, 2));
    } else if (mode !== OutputMode.QUIET) {
      const { value } = result;
      console.log(formatter.formatSummary(
        "Documentation coverage",
        `${value.coveragePercent.toFixed(1)}%`,
        value.valid
      ));
      if (value.undocumented.length > 0 && (mode === OutputMode.VERBOSE || !value.valid)) {
        console.log("\nUndocumented files:");
        for (const file of value.undocumented.slice(0, 10)) {
          console.log(`  - ${file}`);
        }
        if (value.undocumented.length > 10) {
          console.log(`  ... and ${value.undocumented.length - 10} more`);
        }
      }
      if (value.brokenLinks.length > 0) {
        console.log("\nBroken links:");
        for (const link of value.brokenLinks) {
          console.log(`  - ${link}`);
        }
      }
    }
    process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
  });
  return command;
}

// src/index.ts
function createProgram() {
  const program = new Command4();
  program.name("harness").description("CLI for Harness Engineering toolkit").version(VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
  program.addCommand(createValidateCommand());
  program.addCommand(createCheckDepsCommand());
  program.addCommand(createCheckDocsCommand());
  return program;
}

export {
  ExitCode,
  CLIError,
  handleError,
  findConfigFile,
  loadConfig,
  resolveConfig,
  OutputMode,
  OutputFormatter,
  logger,
  createProgram
};
