"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_test_1 = require("node:test");
const strict_1 = __importDefault(require("node:assert/strict"));
const fs = __importStar(require("node:fs"));
const os = __importStar(require("node:os"));
const path = __importStar(require("node:path"));
const dotenv = __importStar(require("dotenv"));
const init_1 = require("../commands/init");
// ─── envVal ──────────────────────────────────────────────────────────────────
(0, node_test_1.describe)('envVal', () => {
    (0, node_test_1.it)('returns plain value unchanged', () => {
        strict_1.default.equal((0, init_1.envVal)('simple'), 'simple');
    });
    (0, node_test_1.it)('wraps values containing spaces in double quotes', () => {
        strict_1.default.equal((0, init_1.envVal)('in review'), '"in review"');
    });
    (0, node_test_1.it)('wraps values containing # in double quotes', () => {
        strict_1.default.equal((0, init_1.envVal)('hello#comment'), '"hello#comment"');
    });
    (0, node_test_1.it)('wraps values containing single quotes', () => {
        strict_1.default.equal((0, init_1.envVal)("it's"), `"it's"`);
    });
    (0, node_test_1.it)('escapes existing double quotes inside the value', () => {
        strict_1.default.equal((0, init_1.envVal)('say "hi"'), '"say \\"hi\\""');
    });
    (0, node_test_1.it)('returns empty string unchanged', () => {
        strict_1.default.equal((0, init_1.envVal)(''), '');
    });
});
// ─── renderEnv ───────────────────────────────────────────────────────────────
const baseAnswers = {
    provider: 'clickup',
    clickupApiKey: 'pk_abc123',
    clickupTeamId: 'team_456',
    clickupTag: 'myproject',
    clickupPendingStatus: 'pending',
    clickupInReviewStatus: 'review',
    jiraBaseUrl: '',
    jiraEmail: '',
    jiraApiToken: '',
    jiraProject: '',
    jiraLabel: '',
    jiraPendingStatus: '',
    jiraInReviewStatus: '',
    linearApiKey: '',
    linearTeamId: '',
    linearLabel: '',
    linearPendingStatus: '',
    linearInReviewStatus: '',
    mondayApiToken: '',
    mondayBoardId: '',
    mondayStatusColumnId: 'status',
    mondayGroupId: '',
    trelloApiKey: '',
    trelloToken: '',
    trelloBoardId: '',
    trelloLabel: '',
    trelloOpenList: 'To Do',
    trelloPendingList: 'Blocked',
    trelloInProgressList: 'Doing',
    trelloInReviewList: 'In Review',
    trelloOpenStatus: '',
    trelloPendingStatus: '',
    trelloInReviewStatus: '',
    nonCodeTag: '',
    nonCodeClickupTeamId: '',
    nonCodeJiraProject: '',
    nonCodeLinearTeamId: '',
    assigneeTag: '',
    gitRemote: 'origin',
    githubBaseBranch: 'main',
    githubRepo: 'owner/repo',
    agents: 'claude,cursor',
    devNotesMode: 'smart',
    triggerWord: 'aidev-continue',
    thinkingTag: '',
    planningTag: '',
    commentPrefix: '[aidev]',
    aidevEnvExtend: '',
    acceptedTag: '',
    doneStatus: '',
    anthropicApiKey: '',
    anthropicBaseUrl: '',
    claudeModel: '',
    anthropicModel: '',
};
(0, node_test_1.describe)('renderEnv', () => {
    (0, node_test_1.it)('writes non-empty values', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        strict_1.default.ok(out.includes('CLICKUP_API_KEY=pk_abc123'));
        strict_1.default.ok(out.includes('CLICKUP_TEAM_ID=team_456'));
        strict_1.default.ok(out.includes('CLICKUP_TAG=myproject'));
        strict_1.default.ok(out.includes('AGENTS=claude,cursor'));
        strict_1.default.ok(out.includes('GITHUB_REPO=owner/repo'));
    });
    (0, node_test_1.it)('omits empty optional values', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        strict_1.default.ok(!out.includes('ASSIGNEE_TAG'));
    });
    (0, node_test_1.it)('quotes status values that contain spaces', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, clickupInReviewStatus: 'in review' });
        strict_1.default.ok(out.includes('CLICKUP_IN_REVIEW_STATUS="in review"'));
    });
    (0, node_test_1.it)('quotes pending status if it has spaces', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, clickupPendingStatus: 'needs info' });
        strict_1.default.ok(out.includes('CLICKUP_PENDING_STATUS="needs info"'));
    });
    (0, node_test_1.it)('includes comment before AGENTS', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        strict_1.default.ok(out.includes('# Agents to use'));
    });
    (0, node_test_1.it)('includes default AIDEV_HOOKS_PATH', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        strict_1.default.ok(out.includes('AIDEV_HOOKS_PATH=.aidev/aidev.hooks.ts'));
    });
    (0, node_test_1.it)('includes ACCEPTED_TAG and DONE_STATUS when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, acceptedTag: 'accepted', doneStatus: 'done' });
        strict_1.default.ok(out.includes('ACCEPTED_TAG=accepted'));
        strict_1.default.ok(out.includes('DONE_STATUS=done'));
    });
    (0, node_test_1.it)('renders PLANNING_TAG with the configured value', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, planningTag: 'planning' });
        strict_1.default.ok(out.includes('PLANNING_TAG=planning'));
        strict_1.default.ok(out.includes('# PLANNING_TAG'));
    });
    (0, node_test_1.it)('renders a custom PLANNING_TAG value', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, planningTag: 'breakdown' });
        strict_1.default.ok(out.includes('PLANNING_TAG=breakdown'));
    });
    (0, node_test_1.it)('omits GITHUB_REPO when empty', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, githubRepo: '' });
        strict_1.default.ok(!out.includes('GITHUB_REPO'));
    });
    (0, node_test_1.it)('omits AIDEV_ENV_EXTEND when empty', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, aidevEnvExtend: '' });
        strict_1.default.ok(!out.includes('AIDEV_ENV_EXTEND'));
    });
    (0, node_test_1.it)('includes AIDEV_ENV_EXTEND at the top when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, aidevEnvExtend: '/home/user/.aidev.global' });
        strict_1.default.ok(out.includes('AIDEV_ENV_EXTEND=/home/user/.aidev.global'));
        // must appear before provider config
        const extIdx = out.indexOf('AIDEV_ENV_EXTEND');
        const provIdx = out.indexOf('PROVIDER=');
        strict_1.default.ok(extIdx < provIdx, 'AIDEV_ENV_EXTEND should appear before PROVIDER');
    });
    (0, node_test_1.it)('includes a descriptive comment before AIDEV_ENV_EXTEND when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, aidevEnvExtend: '/home/user/.aidev.global' });
        strict_1.default.ok(out.includes('# Global env base'));
    });
});
// ─── renderEnv Anthropic SDK runner ──────────────────────────────────────────
(0, node_test_1.describe)('renderEnv anthropic-sdk runner', () => {
    (0, node_test_1.it)('writes ANTHROPIC_API_KEY and ANTHROPIC_MODEL when anthropic-sdk is in AGENTS', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'anthropic-sdk,claude',
            anthropicApiKey: 'sk-ant-key-1',
            anthropicModel: 'claude-opus-4-6',
        });
        strict_1.default.ok(out.includes('AGENTS=anthropic-sdk,claude'));
        strict_1.default.ok(out.includes('ANTHROPIC_API_KEY=sk-ant-key-1'));
        strict_1.default.ok(out.includes('ANTHROPIC_MODEL=claude-opus-4-6'));
    });
    (0, node_test_1.it)('writes ANTHROPIC_BASE_URL when set, otherwise leaves a commented placeholder', () => {
        const withBase = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'anthropic-sdk',
            anthropicApiKey: 'sk',
            anthropicBaseUrl: 'https://proxy.example.com',
            anthropicModel: 'claude-opus-4-6',
        });
        strict_1.default.ok(withBase.includes('ANTHROPIC_BASE_URL=https://proxy.example.com'));
        const withoutBase = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'anthropic-sdk',
            anthropicApiKey: 'sk',
            anthropicModel: 'claude-opus-4-6',
        });
        strict_1.default.ok(!/^ANTHROPIC_BASE_URL=/m.test(withoutBase));
        strict_1.default.ok(withoutBase.includes('# ANTHROPIC_BASE_URL'));
    });
    (0, node_test_1.it)('defaults ANTHROPIC_MODEL to claude-opus-4-6 when blank', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'anthropic-sdk',
            anthropicApiKey: 'sk',
            anthropicModel: '',
        });
        strict_1.default.ok(out.includes('ANTHROPIC_MODEL=claude-opus-4-6'));
    });
    (0, node_test_1.it)('quotes a comma-containing ANTHROPIC_API_KEY value so dotenv keeps it as one entry', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'anthropic-sdk',
            anthropicApiKey: 'sk-1,sk-2,sk-3',
            anthropicModel: 'claude-opus-4-6',
        });
        // commas don't trigger envVal's quoting (only whitespace/quotes/#), so a raw line is fine
        const parsed = dotenv.parse(out);
        strict_1.default.equal(parsed.ANTHROPIC_API_KEY, 'sk-1,sk-2,sk-3');
    });
    (0, node_test_1.it)('omits Anthropic SDK keys when anthropic-sdk is not in AGENTS', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, agents: 'cursor' });
        strict_1.default.ok(!out.includes('ANTHROPIC_API_KEY'));
        strict_1.default.ok(!out.includes('ANTHROPIC_MODEL'));
        strict_1.default.ok(!out.includes('ANTHROPIC_BASE_URL'));
    });
    (0, node_test_1.it)('round-trips Anthropic SDK config through parse + re-render', () => {
        const answers = {
            ...baseAnswers,
            agents: 'anthropic-sdk,claude',
            anthropicApiKey: 'sk-1,sk-2',
            anthropicBaseUrl: 'https://proxy.example.com',
            anthropicModel: 'claude-opus-4-6',
            claudeModel: 'opusplan',
        };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.ANTHROPIC_API_KEY, 'sk-1,sk-2');
        strict_1.default.equal(parsed.ANTHROPIC_BASE_URL, 'https://proxy.example.com');
        strict_1.default.equal(parsed.ANTHROPIC_MODEL, 'claude-opus-4-6');
        strict_1.default.equal(parsed.CLAUDE_MODEL, 'opusplan');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
});
(0, node_test_1.describe)('renderEnv claude CLI runner', () => {
    (0, node_test_1.it)('writes CLAUDE_MODEL defaulting to opusplan when claude is in AGENTS', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, agents: 'claude,cursor', claudeModel: '' });
        strict_1.default.ok(out.includes('CLAUDE_MODEL=opusplan'));
    });
    (0, node_test_1.it)('writes a custom CLAUDE_MODEL when set', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            agents: 'claude',
            claudeModel: 'claude-sonnet-4-6',
        });
        strict_1.default.ok(out.includes('CLAUDE_MODEL=claude-sonnet-4-6'));
    });
    (0, node_test_1.it)('omits CLAUDE_MODEL when claude is not in AGENTS', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, agents: 'cursor,codex' });
        strict_1.default.ok(!out.includes('CLAUDE_MODEL'));
    });
});
// ─── renderEnv local provider ─────────────────────────────────────────────────
(0, node_test_1.describe)('renderEnv local provider', () => {
    (0, node_test_1.it)('writes PROVIDER=local with comment about .aidev/tasks', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, provider: 'local' });
        strict_1.default.ok(out.includes('PROVIDER=local'));
        strict_1.default.ok(out.includes('.aidev/tasks'));
    });
    (0, node_test_1.it)('does not include ClickUp or Jira keys for local provider', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, provider: 'local' });
        strict_1.default.ok(!out.includes('CLICKUP_API_KEY'));
        strict_1.default.ok(!out.includes('JIRA_BASE_URL'));
    });
});
(0, node_test_1.describe)('renderEnv monday provider', () => {
    (0, node_test_1.it)('writes PROVIDER=monday and Monday keys', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'monday',
            mondayApiToken: 'tok',
            mondayBoardId: '123',
            mondayStatusColumnId: 'status',
            mondayGroupId: 'grp',
            clickupPendingStatus: 'Working on it',
            clickupInReviewStatus: 'Done',
        });
        strict_1.default.ok(out.includes('PROVIDER=monday'));
        strict_1.default.ok(out.includes('MONDAY_API_TOKEN=tok'));
        strict_1.default.ok(out.includes('MONDAY_BOARD_ID=123'));
        strict_1.default.ok(out.includes('CLICKUP_PENDING_STATUS='));
        strict_1.default.ok(out.includes('CLICKUP_IN_REVIEW_STATUS='));
    });
    (0, node_test_1.it)('still includes shared config (AGENTS, GIT_REMOTE, etc.)', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'monday',
            mondayApiToken: 'x',
            mondayBoardId: '1',
            mondayStatusColumnId: 'status',
            mondayGroupId: '',
        });
        strict_1.default.ok(out.includes('AGENTS=claude,cursor'));
        strict_1.default.ok(out.includes('GIT_REMOTE=origin'));
        strict_1.default.ok(out.includes('GITHUB_BASE_BRANCH=main'));
    });
});
// ─── getWindowsCursorInitMessage ──────────────────────────────────────────────
(0, node_test_1.describe)('getWindowsCursorInitMessage', () => {
    const winNoAgent = { isWindows: true, commandExists: (_) => false };
    const winWithAgent = { isWindows: true, commandExists: (c) => c === 'agent' };
    const notWin = { isWindows: false, commandExists: (_) => false };
    (0, node_test_1.it)('returns null when not Windows', () => {
        strict_1.default.equal((0, init_1.getWindowsCursorInitMessage)('cursor,claude', notWin), null);
    });
    (0, node_test_1.it)('returns null when cursor not in agents list', () => {
        strict_1.default.equal((0, init_1.getWindowsCursorInitMessage)('claude,windsurf', winNoAgent), null);
    });
    (0, node_test_1.it)('returns null when Windows and agent exists', () => {
        strict_1.default.equal((0, init_1.getWindowsCursorInitMessage)('cursor,claude', winWithAgent), null);
    });
    (0, node_test_1.it)('returns message when Windows, cursor in list, and agent missing', () => {
        const msg = (0, init_1.getWindowsCursorInitMessage)('cursor,claude', winNoAgent);
        strict_1.default.ok(msg !== null);
        strict_1.default.ok(msg.includes('Windows: Cursor Agent CLI'));
        strict_1.default.ok(msg.includes('cursor.com/install?win32=true'));
        strict_1.default.ok(msg.includes('iex'));
        strict_1.default.ok(msg.includes('agent --version'));
    });
    (0, node_test_1.it)('handles agents string with spaces', () => {
        const msg = (0, init_1.getWindowsCursorInitMessage)('claude , cursor , windsurf', winNoAgent);
        strict_1.default.ok(msg !== null);
        strict_1.default.ok(msg.includes('cursor.com/install'));
    });
});
// ─── existing env round-trip (edit flow) ─────────────────────────────────────
//
// Core guarantee: when the user presses Enter for every prompt during a
// re-edit, the written .env.aidev must be byte-for-byte identical to the
// original.  We verify this by:
//   1. renderEnv(answers) → parse with dotenv → feed parsed values back in →
//      renderEnv again → assert equal.
// This mirrors exactly what initCommand does: it reads the file with
// dotenv.parse() and uses the result as the `defaultVal` for each prompt.
/** Reconstruct an Answers object from a dotenv-parsed record (same logic as initCommand). */
function answersFromParsed(p, folderName = 'myproject') {
    return {
        provider: (p.PROVIDER || 'clickup'),
        aidevEnvExtend: p.AIDEV_ENV_EXTEND || '',
        clickupApiKey: p.CLICKUP_API_KEY || '',
        clickupTeamId: p.CLICKUP_TEAM_ID || '',
        clickupTag: p.CLICKUP_TAG || folderName,
        clickupPendingStatus: p.CLICKUP_PENDING_STATUS || 'pending',
        clickupInReviewStatus: p.CLICKUP_IN_REVIEW_STATUS || 'review',
        jiraBaseUrl: p.JIRA_BASE_URL || '',
        jiraEmail: p.JIRA_EMAIL || '',
        jiraApiToken: p.JIRA_API_TOKEN || '',
        jiraProject: p.JIRA_PROJECT || '',
        jiraLabel: p.JIRA_LABEL || folderName,
        jiraPendingStatus: p.JIRA_PENDING_STATUS || 'To Do',
        jiraInReviewStatus: p.JIRA_IN_REVIEW_STATUS || 'In Review',
        linearApiKey: p.LINEAR_API_KEY || '',
        linearTeamId: p.LINEAR_TEAM_ID || '',
        linearLabel: p.LINEAR_LABEL || folderName,
        linearPendingStatus: p.LINEAR_PENDING_STATUS || 'Pending',
        linearInReviewStatus: p.LINEAR_IN_REVIEW_STATUS || 'In Review',
        mondayApiToken: p.MONDAY_API_TOKEN || '',
        mondayBoardId: p.MONDAY_BOARD_ID || '',
        mondayStatusColumnId: p.MONDAY_STATUS_COLUMN_ID || 'status',
        mondayGroupId: p.MONDAY_GROUP_ID || '',
        trelloApiKey: p.TRELLO_API_KEY || '',
        trelloToken: p.TRELLO_TOKEN || '',
        trelloBoardId: p.TRELLO_BOARD_ID || '',
        trelloLabel: p.TRELLO_LABEL || folderName,
        trelloOpenList: p.TRELLO_OPEN_LIST || 'To Do',
        trelloPendingList: p.TRELLO_PENDING_LIST || 'Blocked',
        trelloInProgressList: p.TRELLO_IN_PROGRESS_LIST || 'Doing',
        trelloInReviewList: p.TRELLO_IN_REVIEW_LIST || 'In Review',
        trelloOpenStatus: p.TRELLO_OPEN_STATUS || '',
        trelloPendingStatus: p.TRELLO_PENDING_STATUS || '',
        trelloInReviewStatus: p.TRELLO_IN_REVIEW_STATUS || '',
        nonCodeTag: p.NON_CODE_TAG || '',
        nonCodeClickupTeamId: p.NON_CODE_CLICKUP_TEAM_ID || '',
        nonCodeJiraProject: p.NON_CODE_JIRA_PROJECT || '',
        nonCodeLinearTeamId: p.NON_CODE_LINEAR_TEAM_ID || '',
        assigneeTag: p.ASSIGNEE_TAG || '',
        gitRemote: p.GIT_REMOTE || 'origin',
        githubBaseBranch: p.GITHUB_BASE_BRANCH || 'main',
        githubRepo: p.GITHUB_REPO || '',
        agents: p.AGENTS || '',
        anthropicApiKey: p.ANTHROPIC_API_KEY || '',
        anthropicBaseUrl: p.ANTHROPIC_BASE_URL || '',
        anthropicModel: p.ANTHROPIC_MODEL || '',
        claudeModel: p.CLAUDE_MODEL || '',
        devNotesMode: p.DEV_NOTES_MODE || 'smart',
        triggerWord: p.AIDEV_TRIGGER_WORD || 'aidev-continue',
        thinkingTag: p.THINKING_TAG || '',
        planningTag: p.PLANNING_TAG || '',
        commentPrefix: p.AIDEV_COMMENT_PREFIX || '[aidev]',
        acceptedTag: p.ACCEPTED_TAG || '',
        doneStatus: p.DONE_STATUS || '',
    };
}
(0, node_test_1.describe)('existing env round-trip (edit flow)', () => {
    (0, node_test_1.it)('dotenv.parse recovers all ClickUp field values from rendered output', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        const p = dotenv.parse(out);
        strict_1.default.equal(p.PROVIDER, 'clickup');
        strict_1.default.equal(p.CLICKUP_API_KEY, 'pk_abc123');
        strict_1.default.equal(p.CLICKUP_TEAM_ID, 'team_456');
        strict_1.default.equal(p.CLICKUP_TAG, 'myproject');
        strict_1.default.equal(p.CLICKUP_PENDING_STATUS, 'pending');
        strict_1.default.equal(p.CLICKUP_IN_REVIEW_STATUS, 'review');
        strict_1.default.equal(p.GIT_REMOTE, 'origin');
        strict_1.default.equal(p.GITHUB_BASE_BRANCH, 'main');
        strict_1.default.equal(p.GITHUB_REPO, 'owner/repo');
        strict_1.default.equal(p.AGENTS, 'claude,cursor');
        strict_1.default.equal(p.DEV_NOTES_MODE, 'smart');
        strict_1.default.equal(p.AIDEV_TRIGGER_WORD, 'aidev-continue');
        strict_1.default.equal(p.AIDEV_HOOKS_PATH, '.aidev/aidev.hooks.ts');
    });
    (0, node_test_1.it)('re-rendering ClickUp answers with parsed values produces identical output', () => {
        const first = (0, init_1.renderEnv)(baseAnswers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('re-rendering Jira answers with parsed values produces identical output', () => {
        const jiraAnswers = {
            ...baseAnswers,
            provider: 'jira',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            jiraBaseUrl: 'https://acme.atlassian.net',
            jiraEmail: 'dev@acme.com',
            jiraApiToken: 'tok_secret',
            jiraProject: 'PROJ',
            jiraLabel: 'aidev',
            jiraPendingStatus: 'To Do',
            jiraInReviewStatus: 'In Review',
        };
        const first = (0, init_1.renderEnv)(jiraAnswers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('re-rendering Monday answers with parsed values produces identical output', () => {
        const mondayAnswers = {
            ...baseAnswers,
            provider: 'monday',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            mondayApiToken: 'token123',
            mondayBoardId: '123456',
            mondayStatusColumnId: 'status',
            mondayGroupId: 'topics',
            clickupPendingStatus: 'Working on it',
            clickupInReviewStatus: 'Done',
        };
        const first = (0, init_1.renderEnv)(mondayAnswers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('quoted values (spaces) survive parse → re-render', () => {
        const answers = {
            ...baseAnswers,
            clickupPendingStatus: 'needs info',
            clickupInReviewStatus: 'in review',
        };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        // dotenv strips quotes — parsed values should be plain strings
        strict_1.default.equal(parsed.CLICKUP_PENDING_STATUS, 'needs info');
        strict_1.default.equal(parsed.CLICKUP_IN_REVIEW_STATUS, 'in review');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('AIDEV_ENV_EXTEND survives parse → re-render', () => {
        const answers = { ...baseAnswers, aidevEnvExtend: '/home/user/.aidev.global' };
        const first = (0, init_1.renderEnv)(answers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('assigneeTag survives parse → re-render', () => {
        const answers = { ...baseAnswers, assigneeTag: 'alice <alice@example.com>' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.ASSIGNEE_TAG, 'alice <alice@example.com>');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('changing one field during edit leaves all others unchanged', () => {
        const first = (0, init_1.renderEnv)(baseAnswers);
        const parsed = dotenv.parse(first);
        // Simulate user typing a new value only for CLICKUP_TAG
        const editedAnswers = answersFromParsed(parsed);
        editedAnswers.clickupTag = 'new-tag';
        const second = (0, init_1.renderEnv)(editedAnswers);
        const p2 = dotenv.parse(second);
        strict_1.default.equal(p2.CLICKUP_TAG, 'new-tag');
        // Everything else unchanged
        strict_1.default.equal(p2.CLICKUP_API_KEY, parsed.CLICKUP_API_KEY);
        strict_1.default.equal(p2.CLICKUP_TEAM_ID, parsed.CLICKUP_TEAM_ID);
        strict_1.default.equal(p2.AGENTS, parsed.AGENTS);
        strict_1.default.equal(p2.GIT_REMOTE, parsed.GIT_REMOTE);
        strict_1.default.equal(p2.GITHUB_BASE_BRANCH, parsed.GITHUB_BASE_BRANCH);
    });
    (0, node_test_1.it)('THINKING_TAG survives parse → re-render when set', () => {
        const answers = { ...baseAnswers, thinkingTag: 'think' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.THINKING_TAG, 'think');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('PLANNING_TAG survives parse → re-render when set', () => {
        const answers = { ...baseAnswers, planningTag: 'plan' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.PLANNING_TAG, 'plan');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('empty optional fields are absent from parsed output (no stale keys)', () => {
        const out = (0, init_1.renderEnv)(baseAnswers); // assigneeTag='', thinkingTag='', planningTag='', aidevEnvExtend='', nonCodeTag=''
        const p = dotenv.parse(out);
        strict_1.default.ok(!('ASSIGNEE_TAG' in p), 'ASSIGNEE_TAG should be absent when empty');
        strict_1.default.ok(!('AIDEV_ENV_EXTEND' in p), 'AIDEV_ENV_EXTEND should be absent when empty');
        strict_1.default.ok(!('THINKING_TAG' in p), 'THINKING_TAG should be absent when empty');
        strict_1.default.ok(!('PLANNING_TAG' in p), 'PLANNING_TAG should be absent when empty');
        strict_1.default.ok(!('NON_CODE_TAG' in p), 'NON_CODE_TAG should be absent when empty');
        strict_1.default.ok(!('NON_CODE_CLICKUP_TEAM_ID' in p), 'NON_CODE_CLICKUP_TEAM_ID should be absent when empty');
        strict_1.default.ok(!('NON_CODE_JIRA_PROJECT' in p), 'NON_CODE_JIRA_PROJECT should be absent when empty');
        strict_1.default.ok(!('NON_CODE_LINEAR_TEAM_ID' in p), 'NON_CODE_LINEAR_TEAM_ID should be absent when empty');
    });
});
// ─── non-code tag fields ──────────────────────────────────────────────────────
(0, node_test_1.describe)('renderEnv non-code fields', () => {
    (0, node_test_1.it)('includes NON_CODE_TAG when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, nonCodeTag: 'non-code' });
        strict_1.default.ok(out.includes('NON_CODE_TAG=non-code'));
    });
    (0, node_test_1.it)('omits NON_CODE_TAG when empty', () => {
        const out = (0, init_1.renderEnv)(baseAnswers);
        strict_1.default.ok(!out.includes('NON_CODE_TAG='));
    });
    (0, node_test_1.it)('includes descriptive comment for NON_CODE_TAG', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, nonCodeTag: 'non-code' });
        strict_1.default.ok(out.includes('# NON_CODE_TAG'));
    });
    (0, node_test_1.it)('includes NON_CODE_CLICKUP_TEAM_ID when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, nonCodeTag: 'non-code', nonCodeClickupTeamId: '999' });
        strict_1.default.ok(out.includes('NON_CODE_CLICKUP_TEAM_ID=999'));
    });
    (0, node_test_1.it)('omits NON_CODE_CLICKUP_TEAM_ID when empty', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, nonCodeTag: 'non-code' });
        strict_1.default.ok(!out.includes('NON_CODE_CLICKUP_TEAM_ID'));
    });
    (0, node_test_1.it)('includes NON_CODE_JIRA_PROJECT when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, nonCodeTag: 'non-code', nonCodeJiraProject: 'OPS' });
        strict_1.default.ok(out.includes('NON_CODE_JIRA_PROJECT=OPS'));
    });
    (0, node_test_1.it)('NON_CODE_TAG survives parse → re-render', () => {
        const answers = { ...baseAnswers, nonCodeTag: 'non-code' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.NON_CODE_TAG, 'non-code');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('NON_CODE_CLICKUP_TEAM_ID survives parse → re-render', () => {
        const answers = { ...baseAnswers, nonCodeTag: 'ops', nonCodeClickupTeamId: '777' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.NON_CODE_CLICKUP_TEAM_ID, '777');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('NON_CODE_JIRA_PROJECT survives parse → re-render', () => {
        const answers = { ...baseAnswers, nonCodeTag: 'ops', nonCodeJiraProject: 'DEVOPS' };
        const first = (0, init_1.renderEnv)(answers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.NON_CODE_JIRA_PROJECT, 'DEVOPS');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('includes NON_CODE_LINEAR_TEAM_ID when set', () => {
        const out = (0, init_1.renderEnv)({ ...baseAnswers, provider: 'linear', nonCodeTag: 'non-code', nonCodeLinearTeamId: 'team-uuid' });
        strict_1.default.ok(out.includes('NON_CODE_LINEAR_TEAM_ID=team-uuid'));
    });
});
// ─── renderEnv Linear provider ───────────────────────────────────────────────
(0, node_test_1.describe)('renderEnv Linear provider', () => {
    (0, node_test_1.it)('writes PROVIDER=linear and LINEAR_* keys', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'linear',
            linearApiKey: 'lin_api_xxx',
            linearTeamId: 'team-uuid',
            linearLabel: 'aidev',
            linearPendingStatus: 'Backlog',
            linearInReviewStatus: 'In Review',
        });
        strict_1.default.ok(out.includes('PROVIDER=linear'));
        strict_1.default.ok(out.includes('LINEAR_API_KEY=lin_api_xxx'));
        strict_1.default.ok(out.includes('LINEAR_TEAM_ID=team-uuid'));
        strict_1.default.ok(out.includes('LINEAR_LABEL=aidev'));
        strict_1.default.ok(out.includes('LINEAR_PENDING_STATUS=Backlog'));
        strict_1.default.ok(out.includes('LINEAR_IN_REVIEW_STATUS'));
        strict_1.default.ok(out.includes('In Review'));
    });
    (0, node_test_1.it)('re-rendering Linear answers with parsed values produces identical output', () => {
        const linearAnswers = {
            ...baseAnswers,
            provider: 'linear',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            linearApiKey: 'lin_api_xxx',
            linearTeamId: 'team-uuid',
            linearLabel: 'myproject',
            linearPendingStatus: 'Backlog',
            linearInReviewStatus: 'In Review',
        };
        const first = (0, init_1.renderEnv)(linearAnswers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
});
// ─── renderEnv Trello provider ───────────────────────────────────────────────
(0, node_test_1.describe)('renderEnv Trello provider', () => {
    (0, node_test_1.it)('writes PROVIDER=trello and TRELLO_* keys', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'trello',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            trelloApiKey: 'key123',
            trelloToken: 'tok456',
            trelloBoardId: 'board789',
            trelloLabel: 'myproject',
            trelloOpenList: 'To Do',
            trelloPendingList: 'Blocked',
            trelloInProgressList: 'Doing',
            trelloInReviewList: 'In Review',
        });
        strict_1.default.ok(out.includes('PROVIDER=trello'));
        strict_1.default.ok(out.includes('TRELLO_API_KEY=key123'));
        strict_1.default.ok(out.includes('TRELLO_TOKEN=tok456'));
        strict_1.default.ok(out.includes('TRELLO_BOARD_ID=board789'));
        strict_1.default.ok(out.includes('TRELLO_LABEL=myproject'));
        strict_1.default.ok(out.includes('TRELLO_OPEN_LIST="To Do"'));
        strict_1.default.ok(out.includes('TRELLO_PENDING_LIST=Blocked'));
        strict_1.default.ok(out.includes('TRELLO_IN_PROGRESS_LIST=Doing'));
        strict_1.default.ok(out.includes('TRELLO_IN_REVIEW_LIST="In Review"'));
    });
    (0, node_test_1.it)('omits semantic status keys when empty', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'trello',
            trelloApiKey: 'k',
            trelloToken: 't',
            trelloBoardId: 'b',
        });
        strict_1.default.ok(!out.includes('TRELLO_OPEN_STATUS'));
        strict_1.default.ok(!out.includes('TRELLO_PENDING_STATUS'));
        strict_1.default.ok(!out.includes('TRELLO_IN_REVIEW_STATUS'));
    });
    (0, node_test_1.it)('does not include ClickUp keys when provider is trello', () => {
        const out = (0, init_1.renderEnv)({
            ...baseAnswers,
            provider: 'trello',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            trelloApiKey: 'k',
            trelloToken: 't',
            trelloBoardId: 'b',
        });
        strict_1.default.ok(!out.includes('CLICKUP_API_KEY'));
        strict_1.default.ok(!out.includes('CLICKUP_PENDING_STATUS'));
    });
    (0, node_test_1.it)('re-rendering Trello answers with parsed values produces identical output', () => {
        const trelloAnswers = {
            ...baseAnswers,
            provider: 'trello',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            trelloApiKey: 'key123',
            trelloToken: 'tok456',
            trelloBoardId: 'board789',
            trelloLabel: 'myproject',
            trelloOpenList: 'To Do',
            trelloPendingList: 'Blocked',
            trelloInProgressList: 'Doing',
            trelloInReviewList: 'In Review',
        };
        const first = (0, init_1.renderEnv)(trelloAnswers);
        const second = (0, init_1.renderEnv)(answersFromParsed(dotenv.parse(first)));
        strict_1.default.equal(second, first);
    });
    (0, node_test_1.it)('preserves customized semantic statuses through round-trip', () => {
        const trelloAnswers = {
            ...baseAnswers,
            provider: 'trello',
            clickupApiKey: '',
            clickupTeamId: '',
            clickupTag: '',
            clickupPendingStatus: '',
            clickupInReviewStatus: '',
            trelloApiKey: 'k',
            trelloToken: 't',
            trelloBoardId: 'b',
            trelloLabel: 'lbl',
            trelloOpenList: 'To Do',
            trelloPendingList: 'Blocked',
            trelloInProgressList: 'Doing',
            trelloInReviewList: 'In Review',
            trelloOpenStatus: 'todo',
            trelloPendingStatus: 'waiting',
            trelloInReviewStatus: 'qa',
        };
        const first = (0, init_1.renderEnv)(trelloAnswers);
        const parsed = dotenv.parse(first);
        strict_1.default.equal(parsed.TRELLO_OPEN_STATUS, 'todo');
        strict_1.default.equal(parsed.TRELLO_PENDING_STATUS, 'waiting');
        strict_1.default.equal(parsed.TRELLO_IN_REVIEW_STATUS, 'qa');
        const second = (0, init_1.renderEnv)(answersFromParsed(parsed));
        strict_1.default.equal(second, first);
    });
});
// ─── ensureHooksBoilerplate ─────────────────────────────────────────────────
(0, node_test_1.describe)('ensureHooksBoilerplate', () => {
    let tmp;
    (0, node_test_1.beforeEach)(() => {
        tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-init-hooks-'));
    });
    (0, node_test_1.afterEach)(() => {
        fs.rmSync(tmp, { recursive: true, force: true });
    });
    (0, node_test_1.it)('creates .aidev/aidev.hooks.ts with interfaces and stub hooks', () => {
        (0, init_1.ensureHooksBoilerplate)(tmp);
        const p = path.join(tmp, '.aidev', 'aidev.hooks.ts');
        strict_1.default.ok(fs.existsSync(p));
        const body = fs.readFileSync(p, 'utf8');
        strict_1.default.ok(body.includes('export async function'));
        strict_1.default.ok(body.includes('async function beforeRun'));
        strict_1.default.ok(body.includes('interface RunContext'));
        strict_1.default.ok(body.includes('beforeResolveConflicts'));
    });
    (0, node_test_1.it)('does not overwrite an existing hooks file', () => {
        const aidevDir = path.join(tmp, '.aidev');
        fs.mkdirSync(aidevDir, { recursive: true });
        const p = path.join(aidevDir, 'aidev.hooks.ts');
        fs.writeFileSync(p, '// custom', 'utf8');
        (0, init_1.ensureHooksBoilerplate)(tmp);
        strict_1.default.equal(fs.readFileSync(p, 'utf8'), '// custom');
    });
});
// ─── ensureGitignore ─────────────────────────────────────────────────────────
function withTmpDir(fn) {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-test-'));
    try {
        fn(dir);
    }
    finally {
        fs.rmSync(dir, { recursive: true, force: true });
    }
}
(0, node_test_1.describe)('ensureGitignore', () => {
    (0, node_test_1.it)('creates .gitignore with required entries when file does not exist', () => {
        withTmpDir((dir) => {
            (0, init_1.ensureGitignore)(dir);
            const content = fs.readFileSync(path.join(dir, '.gitignore'), 'utf8');
            strict_1.default.ok(content.includes('.env.*'));
            strict_1.default.ok(content.includes('*.log'));
            strict_1.default.ok(content.includes('.aidev/assets/'));
            strict_1.default.ok(!content.includes('.aidev/\n'));
        });
    });
    (0, node_test_1.it)('appends missing entries to an existing .gitignore', () => {
        withTmpDir((dir) => {
            fs.writeFileSync(path.join(dir, '.gitignore'), 'node_modules/\n');
            (0, init_1.ensureGitignore)(dir);
            const content = fs.readFileSync(path.join(dir, '.gitignore'), 'utf8');
            strict_1.default.ok(content.includes('node_modules/'));
            strict_1.default.ok(content.includes('.env.*'));
            strict_1.default.ok(content.includes('*.log'));
            strict_1.default.ok(content.includes('.aidev/assets/'));
        });
    });
    (0, node_test_1.it)('does not duplicate .env.* if already present', () => {
        withTmpDir((dir) => {
            fs.writeFileSync(path.join(dir, '.gitignore'), '.env.*\n*.log\n');
            (0, init_1.ensureGitignore)(dir);
            const content = fs.readFileSync(path.join(dir, '.gitignore'), 'utf8');
            strict_1.default.equal((content.match(/\.env\.\*/g) ?? []).length, 1);
            strict_1.default.equal((content.match(/\*\.log/g) ?? []).length, 1);
        });
    });
    (0, node_test_1.it)('handles existing file without trailing newline', () => {
        withTmpDir((dir) => {
            fs.writeFileSync(path.join(dir, '.gitignore'), 'dist');
            (0, init_1.ensureGitignore)(dir);
            const content = fs.readFileSync(path.join(dir, '.gitignore'), 'utf8');
            strict_1.default.ok(content.includes('\n.env.*'));
        });
    });
    (0, node_test_1.it)('replaces the legacy .aidev ignore rule with .aidev/assets/', () => {
        withTmpDir((dir) => {
            fs.writeFileSync(path.join(dir, '.gitignore'), '.aidev/\n');
            (0, init_1.ensureGitignore)(dir);
            const content = fs.readFileSync(path.join(dir, '.gitignore'), 'utf8');
            strict_1.default.ok(content.includes('.aidev/assets/'));
            strict_1.default.ok(!content.includes('.aidev/\n'));
        });
    });
});
//# sourceMappingURL=init.test.js.map