/**
 * commands/standards.mjs — `apt-tools standards <project-dir> [--profile <n>]` (C20).
 *
 * Zero-orchestration mode: emits a read-only bundle of the project's
 * standards (constitution, profile chain, feature registry, design tokens,
 * design taste) in a single JSON envelope. Mirrors the TS surface in
 * src/standalone/index.ts — they share the same bundle shape and the same
 * whole-bundle-fail semantics for missing project-dir and invalid profile.
 *
 * The CLI reimplements the discovery logic inline rather than importing the
 * TS module so the kernel stays dep-free of the schema bundle (the feature
 * registry is schema-validated only by the TS programmatic surface; the CLI
 * does a shallow JSON parse for shape-insensitive passthrough).
 *
 * Output modes:
 *   - Piped / --json / --no-color: JSON envelope.
 *   - TTY + no --json: human-readable summary via section/bullet.
 */
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { deriveKeywords, normalizeTitle, parseAgentsMd } from '../constitution/parse.mjs';
import { createCoordinationStore } from '../coordination/store.mjs';
import { bullet, section, supportsColor } from '../output-style/index.mjs';
import { resolveProfile } from '../profile/resolve.mjs';
import { parseFlags } from '../util/args.mjs';
import { err, exitWith, ok, rawText } from '../util/result.mjs';
const AGENTS_SEARCH = ['AGENTS.md', 'agents.md', 'CLAUDE.md', 'claude.md'];
/**
 * CLI entry: `apt-tools standards <project-dir> [--profile <name>] [--json] [--no-color]`.
 */
export function cmdStandards(projectDir, extraArgs) {
    if (!projectDir) {
        return err('Usage: apt-tools standards <project-dir> [--profile <name>] [--json] [--no-color]');
    }
    const flags = parseFlags(extraArgs ?? []);
    const profileName = flags.get('profile');
    const jsonFlag = flags.get('json') === true;
    const noColor = flags.get('no-color') === true;
    const absProjectDir = resolve(projectDir);
    if (!existsSync(absProjectDir)) {
        return exitWith({
            status: 'fail',
            command: 'standards',
            reason: `project directory not found: ${absProjectDir}`,
            project_dir: absProjectDir,
        }, 1);
    }
    // Shared-root lookup for worktree awareness. Matches
    // commands/constitution.mjs:40-44 and standalone/index.ts.
    let sharedRoot;
    try {
        sharedRoot = createCoordinationStore(absProjectDir).resolveSharedRoot();
    }
    catch {
        sharedRoot = absProjectDir;
    }
    // --- Constitution -------------------------------------------------------
    const constitution = readConstitution(sharedRoot);
    // --- Profile ------------------------------------------------------------
    let profile = null;
    let profilePrinciples = [];
    if (typeof profileName === 'string' && profileName.length > 0) {
        const result = resolveProfile(absProjectDir, profileName);
        if (!result || result.ok !== true) {
            return exitWith({
                status: 'fail',
                command: 'standards',
                reason: (result && result.reason) || 'profile resolution failed',
                errors: result && result.errors,
                chain: result && result.chain,
                profile: profileName,
            }, 1);
        }
        const resolved = result.resolved || {};
        const resolvedName = typeof resolved.name === 'string' ? resolved.name : profileName;
        const obligations = Array.isArray(resolved.obligations) ? resolved.obligations : [];
        profilePrinciples = obligations.map((o) => ({
            id: typeof o.id === 'string' ? o.id : '',
            title: typeof o.title === 'string' ? o.title : '',
            text: typeof o.text === 'string' ? o.text : '',
            source: { section: `profile:${resolvedName}`, line: -1 },
            keywords: deriveKeywords(`${typeof o.title === 'string' ? o.title : ''} ${typeof o.text === 'string' ? o.text : ''}`),
            severity: typeof o.severity === 'string' ? o.severity : 'obligation',
        }));
        profile = {
            name: resolvedName,
            chain: Array.isArray(result.chain) ? result.chain : [],
            resolved,
        };
    }
    // Merge profile obligations into AGENTS.md principles, AGENTS.md wins on
    // normalized-title collision.
    let principles = constitution.principles;
    if (profilePrinciples.length > 0) {
        const seen = new Set(principles.map((p) => normalizeTitle(p.title)));
        const merged = [...principles];
        for (const p of profilePrinciples) {
            const key = normalizeTitle(p.title);
            if (!key || seen.has(key))
                continue;
            seen.add(key);
            merged.push(p);
        }
        principles = merged;
    }
    // --- Feature registry (shallow JSON passthrough) ------------------------
    const feature_registry = readFeatureRegistryShallow(sharedRoot);
    // --- Design tokens ------------------------------------------------------
    const design_tokens = readDesignTokens(sharedRoot);
    // --- Design taste -------------------------------------------------------
    const design_taste = readDesignTaste(sharedRoot);
    const bundle = {
        project_dir: absProjectDir,
        constitution: {
            source: constitution.source,
            count: principles.length,
            principles,
        },
        profile,
        feature_registry,
        design_tokens,
        design_taste,
    };
    // Decide render mode. Default (no --json, no TTY, or --no-color) → JSON.
    // Human mode only when a real TTY is attached and the caller did not ask
    // for JSON explicitly.
    const useHuman = !jsonFlag && !noColor && Boolean(process.stdout && process.stdout.isTTY);
    if (useHuman) {
        return rawText(renderHuman(bundle), 0);
    }
    return ok({ status: 'ok', command: 'standards', ...bundle });
}
// ---------------------------------------------------------------------------
// Section readers
// ---------------------------------------------------------------------------
function readConstitution(sharedRoot) {
    for (const filename of AGENTS_SEARCH) {
        const path = join(sharedRoot, filename);
        if (!existsSync(path))
            continue;
        try {
            const md = readFileSync(path, 'utf-8');
            return { source: filename, principles: parseAgentsMd(md) };
        }
        catch {
            return { source: filename, principles: [] };
        }
    }
    return { source: null, principles: [] };
}
function readFeatureRegistryShallow(sharedRoot) {
    const dir = join(sharedRoot, '.aperant', 'features');
    if (!existsSync(dir))
        return null;
    let entries;
    try {
        entries = readdirSync(dir);
    }
    catch {
        return null;
    }
    const areas = [];
    const invalid = [];
    for (const name of entries) {
        if (!name.endsWith('.json'))
            continue;
        const path = join(dir, name);
        let isFile = false;
        try {
            isFile = statSync(path).isFile();
        }
        catch {
            continue;
        }
        if (!isFile)
            continue;
        let raw;
        try {
            raw = readFileSync(path, 'utf-8');
        }
        catch (err) {
            invalid.push({
                path,
                errors: [{ path: '<file>', message: `unreadable: ${err.message}` }],
            });
            continue;
        }
        let parsed;
        try {
            parsed = JSON.parse(raw);
        }
        catch (err) {
            invalid.push({
                path,
                errors: [{ path: '<root>', message: `invalid JSON: ${err.message}` }],
            });
            continue;
        }
        // Shallow passthrough — the CLI is dep-free of the schema bundle.
        // Programmatic consumers get full zod validation via standalone/index.ts.
        areas.push(parsed);
    }
    return { count: areas.length, areas, invalid };
}
function readDesignTokens(sharedRoot) {
    const path = join(sharedRoot, '.aperant', 'design', 'tokens.json');
    if (!existsSync(path))
        return null;
    try {
        const raw = readFileSync(path, 'utf-8');
        const parsed = JSON.parse(raw);
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
            return parsed;
        }
        return null;
    }
    catch {
        return null;
    }
}
function readDesignTaste(sharedRoot) {
    const path = join(sharedRoot, '.aperant', 'design', 'taste.yaml');
    if (!existsSync(path))
        return null;
    try {
        return { raw: readFileSync(path, 'utf-8') };
    }
    catch {
        return null;
    }
}
// ---------------------------------------------------------------------------
// Human-readable render
// ---------------------------------------------------------------------------
function renderHuman(bundle) {
    const color = supportsColor();
    const opts = { color };
    const lines = [];
    lines.push(section('Aperant standards discovery', opts));
    const src = bundle.constitution.source;
    const pcount = bundle.constitution.count;
    lines.push(bullet(`constitution: ${src ?? 'absent'} (${pcount} principles)`, undefined, opts));
    if (bundle.profile) {
        const chain = bundle.profile.chain.join(' → ');
        lines.push(bullet(`profile: ${bundle.profile.name} [${chain}]`, undefined, opts));
    }
    else {
        lines.push(bullet('profile: none', undefined, opts));
    }
    if (bundle.feature_registry) {
        const validCount = bundle.feature_registry.count;
        const invalidCount = bundle.feature_registry.invalid.length;
        lines.push(bullet(`feature registry: ${validCount + invalidCount} areas (${validCount} valid, ${invalidCount} invalid)`, undefined, opts));
    }
    else {
        lines.push(bullet('feature registry: absent', undefined, opts));
    }
    lines.push(bullet(`design tokens: ${bundle.design_tokens ? 'present' : 'absent'}`, undefined, opts));
    lines.push(bullet(`design taste: ${bundle.design_taste ? 'present' : 'absent'}`, undefined, opts));
    return `${lines.join('\n')}\n`;
}
//# sourceMappingURL=standards.mjs.map