# state-manager.js

    /**
     * MORPH-SPEC State Manager Library
     *
     * Manages state.json for tracking features, progress, agents, and checkpoints.
     * Used both by CLI commands and internal automation.
     */

    import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
    import { join, dirname } from 'path';

    const STATE_FILE_NAME = '.morph/state.json';

    // ============================================================================
    // Core Functions
    // ============================================================================

    /**
     * Get the state file path (looks in current working directory)
     */
    export function getStatePath() {
      return join(process.cwd(), STATE_FILE_NAME);
    }

    /**
     * Check if state file exists
     */
    export function stateExists() {
      return existsSync(getStatePath());
    }

    /**
     * Load state from disk
     * @param {boolean} throwOnError - If false, returns null instead of throwing
     * @returns {Object|null} State object or null
     */
    export function loadState(throwOnError = true) {
      const statePath = getStatePath();

      if (!existsSync(statePath)) {
        if (throwOnError) {
          throw new Error(`State file not found: ${statePath}\nRun 'morph-spec state init' first.`);
        }
        return null;
      }

      try {
        const content = readFileSync(statePath, 'utf8');
        return JSON.parse(content);
      } catch (err) {
        if (throwOnError) {
          throw new Error(`Failed to parse state.json: ${err.message}`);
        }
        return null;
      }
    }

    /**
     * Save state to disk
     * @param {Object} state - State object to save
     */
    export function saveState(state) {
      state.metadata = state.metadata || {};
      state.metadata.lastUpdated = new Date().toISOString();

      const statePath = getStatePath();
      const stateDir = dirname(statePath);

      // Ensure .morph directory exists
      if (!existsSync(stateDir)) {
        mkdirSync(stateDir, { recursive: true });
      }

      writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
    }

    /**
     * Initialize new state file
     * @param {Object} options - Options
     * @param {boolean} options.force - Overwrite existing file
     * @param {string} options.projectName - Project name
     * @param {string} options.projectType - Project type (e.g., 'blazor-server')
     * @returns {Object} Initial state
     */
    export function initState(options = {}) {
      const { force = false, projectName = '{PROJECT_NAME}', projectType = 'blazor-server' } = options;

      if (stateExists() && !force) {
        throw new Error('State file already exists. Use force=true to overwrite.');
      }

      const initialState = {
        version: "2.1.1",
        project: {
          name: projectName,
          type: projectType,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        },
        features: {},
        metadata: {
          totalFeatures: 0,
          completedFeatures: 0,
          totalCostEstimated: 0,
          totalTimeSpent: 0,
          lastUpdated: new Date().toISOString()
        }
      };

      saveState(initialState);
      return initialState;
    }

    // ============================================================================
    // Feature Operations
    // ============================================================================

    /**
     * Get feature from state
     * @param {string} featureName - Feature name
     * @returns {Object|null} Feature object or null
     */
    export function getFeature(featureName) {
      const state = loadState();
      return state.features[featureName] || null;
    }

    /**
     * Create or get feature with default structure
     * @param {string} featureName - Feature name
     * @returns {Object} Feature object
     */
    function ensureFeature(featureName) {
      const state = loadState();

      if (!state.features[featureName]) {
        state.features[featureName] = {
          status: "draft",
          phase: "proposal",
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          activeAgents: [],
          outputs: {
            proposal: { created: false, path: `.morph/project/outputs/${featureName}/proposal.md` },
            spec: { created: false, path: `.morph/project/outputs/${featureName}/spec.md` },
            contracts: { created: false, path: `.morph/project/outputs/${featureName}/contracts.cs` },
            tasks: { created: false, path: `.morph/project/outputs/${featureName}/tasks.json` },
            uiDesignSystem: { created: false, path: `.morph/project/outputs/${featureName}/ui-design-system.md` },
            uiMockups: { created: false, path: `.morph/project/outputs/${featureName}/ui-mockups.md` },
            uiComponents: { created: false, path: `.morph/project/outputs/${featureName}/ui-components.md` },
            uiFlows: { created: false, path: `.morph/project/outputs/${featureName}/ui-flows.md` },
            decisions: { created: false, path: `.morph/project/outputs/${featureName}/decisions.md` },
            recap: { created: false, path: `.morph/project/outputs/${featureName}/recap.md` }
          },
          tasks: {
            total: 0,
            completed: 0,
            inProgress: 0,
            pending: 0
          },
          checkpoints: [],
          costs: {
            estimated: 0,
            approved: false,
            approvedBy: null,
            approvedAt: null
          }
        };

        state.metadata.totalFeatures++;
        saveState(state);
      }

      return state.features[featureName];
    }

    /**
     * Update feature property (supports nested keys like "tasks.completed")
     * @param {string} featureName - Feature name
     * @param {string} key - Property key (supports dot notation)
     * @param {any} value - Value to set
     */
    export function updateFeature(featureName, key, value) {
      ensureFeature(featureName);
      const state = loadState(); // Load AFTER ensuring feature exists

      const keys = key.split('.');
      let target = state.features[featureName];

      for (let i = 0; i < keys.length - 1; i++) {
        if (!target[keys[i]]) {
          target[keys[i]] = {};
        }
        target = target[keys[i]];
      }

      const finalKey = keys[keys.length - 1];
      target[finalKey] = value;
      state.features[featureName].updatedAt = new Date().toISOString();

      saveState(state);
    }

    /**
     * Update multiple feature properties at once
     * @param {string} featureName - Feature name
     * @param {Object} updates - Object with key-value pairs to update
     */
    export function updateFeatureMultiple(featureName, updates) {
      ensureFeature(featureName);
      const state = loadState(); // Load AFTER ensuring feature exists

      for (const [key, value] of Object.entries(updates)) {
        const keys = key.split('.');
        let target = state.features[featureName];

        for (let i = 0; i < keys.length - 1; i++) {
          if (!target[keys[i]]) {
            target[keys[i]] = {};
          }
          target = target[keys[i]];
        }

        const finalKey = keys[keys.length - 1];
        target[finalKey] = value;
      }

      state.features[featureName].updatedAt = new Date().toISOString();
      saveState(state);
    }

    /**
     * Add checkpoint to feature
     * @param {string} featureName - Feature name
     * @param {string} note - Checkpoint note
     * @returns {Object} Checkpoint object
     */
    export function addCheckpoint(featureName, note) {
      ensureFeature(featureName);
      const state = loadState();
      const feature = state.features[featureName];

      const checkpoint = {
        timestamp: new Date().toISOString(),
        phase: feature.phase,
        completedTasks: feature.tasks.completed,
        note: note
      };

      feature.checkpoints.push(checkpoint);
      feature.updatedAt = new Date().toISOString();

      saveState(state);
      return checkpoint;
    }

    /**
     * Add agent to feature
     * @param {string} featureName - Feature name
     * @param {string} agentId - Agent ID
     * @returns {boolean} True if added, false if already exists
     */
    export function addAgent(featureName, agentId) {
      ensureFeature(featureName);
      const state = loadState(); // Load AFTER ensuring feature exists

      if (!state.features[featureName].activeAgents.includes(agentId)) {
        state.features[featureName].activeAgents.push(agentId);
        state.features[featureName].updatedAt = new Date().toISOString();
        saveState(state);
        return true;
      }

      return false;
    }

    /**
     * Remove agent from feature
     * @param {string} featureName - Feature name
     * @param {string} agentId - Agent ID
     * @returns {boolean} True if removed, false if not found
     */
    export function removeAgent(featureName, agentId) {
      const state = loadState();

      if (!state.features[featureName]) {
        throw new Error(`Feature '${featureName}' not found.`);
      }

      const index = state.features[featureName].activeAgents.indexOf(agentId);
      if (index > -1) {
        state.features[featureName].activeAgents.splice(index, 1);
        state.features[featureName].updatedAt = new Date().toISOString();
        saveState(state);
        return true;
      }

      return false;
    }

    /**
     * Mark output as created
     * @param {string} featureName - Feature name
     * @param {string} outputType - Output type (proposal, spec, contracts, etc.)
     */
    export function markOutput(featureName, outputType) {
      ensureFeature(featureName);
      const state = loadState();

      if (!state.features[featureName].outputs[outputType]) {
        throw new Error(`Output type '${outputType}' not valid. Valid types: proposal, spec, contracts, tasks, uiDesignSystem, uiMockups, uiComponents, uiFlows, decisions, recap`);
      }

      state.features[featureName].outputs[outputType].created = true;
      state.features[featureName].updatedAt = new Date().toISOString();

      saveState(state);
    }

    /**
     * List all features
     * @returns {Array} Array of [featureName, featureObject] tuples
     */
    export function listFeatures() {
      const state = loadState();
      return Object.entries(state.features);
    }

    /**
     * Get project summary
     * @returns {Object} Summary with metadata
     */
    export function getSummary() {
      const state = loadState();
      return {
        project: state.project,
        metadata: state.metadata,
        featuresCount: Object.keys(state.features).length
      };
    }