aimapper
Version:
Universal build tool for managing coding agent rules - Support Claude Code, Cursor, Windsurf, GitHub Copilot, and more
90 lines (89 loc) • 3.59 kB
JavaScript
import { Command, Flags } from '@oclif/core';
import { existsSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { ConfigLoader } from '../core/config-loader.js';
import { compareHashes } from '../core/rules-hash.js';
export default class Check extends Command {
static description = 'Check if a rules build is needed by comparing current hash with stored .build_hash';
static examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --quiet',
'<%= config.bin %> <%= command.id %> --source .rules',
];
static flags = {
config: Flags.string({
char: 'c',
default: '.aimap.yml',
description: 'Path to aimap config file',
}),
force: Flags.boolean({
char: 'f',
default: false,
description: 'Run outside project root (skip git root check)',
}),
quiet: Flags.boolean({
char: 'q',
default: false,
description: 'Quiet mode (exit code only: 0 up-to-date, 1 needs build)',
}),
source: Flags.string({
char: 'd',
description: 'Source directory for rules (overrides config)',
}),
};
async run() {
const { flags } = await this.parse(Check);
// Optional: check project root for consistency with other commands
const projectRoot = this.findProjectRoot();
const currentDir = process.cwd();
if (!projectRoot) {
this.error('Not in a git repository. Initialize git first with "git init"', { exit: 1 });
}
if (currentDir !== projectRoot && !flags.force) {
this.error(`Not in project root. Run from ${projectRoot} or use --force flag`, { exit: 1 });
}
const configLoader = new ConfigLoader(flags.config);
const config = configLoader.load();
const sourceDir = flags.source || config.source;
if (!existsSync(sourceDir)) {
this.error(`Source directory "${sourceDir}" does not exist.`, { exit: 1 });
}
let needsBuild = false;
try {
const { computed, needsBuild: nb, stored } = compareHashes(sourceDir);
needsBuild = nb;
if (!flags.quiet) {
if (!stored) {
this.log(`❓ No stored build hash found at ${join(sourceDir, '.build_hash')}`);
this.log(`🧮 Current hash: ${computed}`);
this.log('📦 Build is needed.');
}
else if (needsBuild) {
this.log(`🧮 Current hash: ${computed}`);
this.log(`🔒 Stored hash: ${stored}`);
this.log('📦 Build is needed (hash mismatch).');
}
else {
this.log(`✅ Up-to-date. Current hash matches stored hash: ${computed}`);
}
}
}
catch (error) {
this.error(`Failed to check build status: ${error}`, { exit: 1 });
}
// Handle quiet mode exit code outside try/catch to avoid EEXIT being caught
if (flags.quiet) {
this.exit(needsBuild ? 1 : 0);
}
}
findProjectRoot() {
let currentDir = process.cwd();
const root = resolve('/');
while (currentDir !== root) {
if (existsSync(join(currentDir, '.git')))
return currentDir;
currentDir = resolve(currentDir, '..');
}
return null;
}
}