tdd-guard
Version:
TDD Guard enforces Test-Driven Development principles using Claude Code hooks
97 lines (96 loc) • 3.72 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Config = void 0;
const path_1 = __importDefault(require("path"));
const TEST_RESULTS_FILENAME = 'test.json';
const TODOS_FILENAME = 'todos.json';
const MODIFICATIONS_FILENAME = 'modifications.json';
const LINT_FILENAME = 'lint.json';
const CONFIG_FILENAME = 'config.json';
class Config {
static DEFAULT_DATA_DIR = '.claude/tdd-guard/data';
dataDir;
useSystemClaude;
anthropicApiKey;
modelType;
linterType;
constructor(options) {
const mode = options?.mode ?? 'production';
this.dataDir = this.getDataDir(options);
this.useSystemClaude = this.getUseSystemClaude(options);
this.anthropicApiKey = this.getAnthropicApiKey(options);
this.modelType = this.getModelType(options, mode);
this.linterType = this.getLinterType(options);
}
getDataDir(options) {
// Determine the base directory
const baseDir = options?.projectRoot ?? this.getValidatedClaudeProjectDir();
// If we have a base directory, construct the full path
if (baseDir) {
return path_1.default.join(baseDir, ...Config.DEFAULT_DATA_DIR.split('/'));
}
// Default to relative path
return Config.DEFAULT_DATA_DIR;
}
getUseSystemClaude(options) {
return options?.useSystemClaude ?? process.env.USE_SYSTEM_CLAUDE === 'true';
}
getAnthropicApiKey(options) {
return options?.anthropicApiKey ?? process.env.TDD_GUARD_ANTHROPIC_API_KEY;
}
getModelType(options, mode) {
return (options?.modelType ?? this.getEnvironmentModelType(mode) ?? 'claude_cli');
}
getEnvironmentModelType(mode) {
if (mode === 'test' && process.env.TEST_MODEL_TYPE) {
return process.env.TEST_MODEL_TYPE;
}
return process.env.MODEL_TYPE;
}
get testResultsFilePath() {
return path_1.default.join(this.dataDir, TEST_RESULTS_FILENAME);
}
get todosFilePath() {
return path_1.default.join(this.dataDir, TODOS_FILENAME);
}
get modificationsFilePath() {
return path_1.default.join(this.dataDir, MODIFICATIONS_FILENAME);
}
get lintFilePath() {
return path_1.default.join(this.dataDir, LINT_FILENAME);
}
get configFilePath() {
return path_1.default.join(this.dataDir, CONFIG_FILENAME);
}
getLinterType(options) {
if (options?.linterType) {
return options.linterType.toLowerCase();
}
const envValue = process.env.LINTER_TYPE?.toLowerCase();
return envValue && envValue.trim() !== '' ? envValue : undefined;
}
getValidatedClaudeProjectDir() {
const projectDir = process.env.CLAUDE_PROJECT_DIR;
if (!projectDir) {
return null;
}
// Validate that CLAUDE_PROJECT_DIR is an absolute path
if (!path_1.default.isAbsolute(projectDir)) {
throw new Error('CLAUDE_PROJECT_DIR must be an absolute path');
}
// Validate that CLAUDE_PROJECT_DIR does not contain path traversal
if (projectDir.includes('..')) {
throw new Error('CLAUDE_PROJECT_DIR must not contain path traversal');
}
// Validate that current working directory is within CLAUDE_PROJECT_DIR
const cwd = process.cwd();
if (!cwd.startsWith(projectDir)) {
throw new Error('CLAUDE_PROJECT_DIR must contain the current working directory');
}
return projectDir;
}
}
exports.Config = Config;