import * as a from "path";
import { C as T } from "./config-file-watcher-DbLp_qKw.js";
import { a as n } from "./logger-CTb8_yz9.js";
import * as d from "fs";
import { URI as p } from "vscode-uri";
import { minimatch as I } from "minimatch";
import { a as x, i as _, S as O } from "./steering-Dcn_tjkT.js";
import { p as k, s as R, F as v } from "./parse-front-matter-CXai4UYs.js";
import { P as y, S as w, A as C, a as D, b as S, c as P } from "./config-constants-CsFJbjLP.js";
import * as M from "os";
const l = "[NodeSteeringDocumentSource]";
class W {
  workspaceFolders;
  homeDir;
  workspaceTrusted;
  steeringDirectoryPaths;
  globalSteeringDir;
  constructor(e) {
    this.workspaceFolders = e.workspaceFolders, this.homeDir = e.homeDir, this.workspaceTrusted = e.workspaceTrusted, this.steeringDirectoryPaths = this.workspaceFolders.map(
      (t) => a.join(t, y, w)
    ), this.globalSteeringDir = a.join(this.homeDir, y, w);
  }
  /**
   * Returns steering documents that should always be included.
   * Includes documents where inclusion is 'always' or not specified.
   */
  async getAlwaysIncludedDocuments() {
    return (await this.listContextDocuments()).filter((t) => t.config?.inclusion === "always" || !t.config?.inclusion);
  }
  /**
   * Returns steering documents that match the given file paths based on their fileMatchPattern.
   * @param files Absolute file paths to match against document patterns
   */
  async getMatchedDocuments(e) {
    const t = await Promise.all(e.map((s) => this.getMatchingDocumentsForFile(s)));
    return this.unique(t.flat());
  }
  // ---------------------------------------------------------------------------
  // Private: Document collection
  // ---------------------------------------------------------------------------
  /**
   * Checks for AGENTS.md in the root of a workspace directory.
   */
  async findAgentsMdFiles(e) {
    try {
      const t = a.join(e, C);
      try {
        return await d.promises.access(t), n.debug(`${l} Found AGENTS.md file in ${e}`), [t];
      } catch {
        return n.debug(`${l} No AGENTS.md file found in ${e}`), [];
      }
    } catch (t) {
      return n.warn(`${l} Error searching for AGENTS.md file in ${e}:`, t), [];
    }
  }
  /**
   * Collects all steering file paths from workspace, global, and AGENTS.md sources.
   */
  async listSteeringFilePaths() {
    try {
      await this.ensureDirectoryExists(this.globalSteeringDir);
      const e = await Promise.all(
        this.steeringDirectoryPaths.map(async (r) => {
          try {
            return await d.promises.access(r), await this.findMarkdownFilesRecursively(r);
          } catch {
            return [];
          }
        })
      );
      let t = [];
      try {
        await d.promises.access(this.globalSteeringDir), t = await this.findMarkdownFilesRecursively(this.globalSteeringDir), n.debug(`${l} Found ${t.length} global steering files`);
      } catch (r) {
        n.warn(`${l} Global steering directory not accessible, ignoring:`, r);
      }
      return [...(await Promise.all(
        this.workspaceFolders.map(async (r) => this.findAgentsMdFiles(r))
      )).flat(), ...e.flat(), ...t];
    } catch (e) {
      return n.warn(`${l} Error listing steering document paths:`, e), [];
    }
  }
  /**
   * Loads and parses a .md steering document from disk.
   */
  async loadSteeringDocument(e) {
    try {
      let t, s, r;
      if (e.startsWith(this.globalSteeringDir + a.sep) || e === this.globalSteeringDir) {
        t = this.globalSteeringDir;
        const m = p.file(this.globalSteeringDir);
        s = p.file(e).path.substring(m.path.length + 1).replace(/\.md$/, ""), r = "global";
      } else {
        const m = this.findWorkspaceForFile(e);
        if (!m) {
          n.warn(`${l} File ${e} is not in any workspace`);
          return;
        }
        t = m, s = a.basename(e, ".md"), r = "workspace";
      }
      const o = await d.promises.readFile(e, "utf-8"), c = p.file(e).toString(), h = p.file(t).toString();
      let f, g;
      try {
        const m = k({ schema: x, content: o });
        f = m.frontMatter ? {
          fileMatchPattern: this.extractPattern(m.frontMatter.fileMatchPattern ?? void 0),
          inclusion: m.frontMatter.inclusion
        } : void 0, g = m.content;
      } catch {
        f = void 0, g = o;
      }
      return {
        type: "steering",
        workspaceUri: h,
        id: c,
        displayName: s,
        config: f,
        content: g,
        source: "steering",
        scope: r,
        uri: c
      };
    } catch (t) {
      n.warn(`${l} Steering document load error`, t);
      return;
    }
  }
  /**
   * Loads an AGENTS.md file as a steering document with inclusion set to 'always'.
   */
  async loadAgentsMdDocument(e) {
    try {
      const t = this.findWorkspaceForFile(e);
      if (!t) {
        n.warn(`${l} AGENTS.md file ${e} is not in a workspace folder`);
        return;
      }
      const s = await d.promises.readFile(e, "utf-8"), i = a.relative(t, e), o = p.file(e).toString();
      return {
        type: "steering",
        workspaceUri: p.file(t).toString(),
        id: o,
        displayName: i,
        config: {
          inclusion: "always",
          fileMatchPattern: void 0
        },
        content: s,
        source: "agents-md",
        scope: "workspace",
        // AGENTS.md files are always workspace-scoped
        uri: o
      };
    } catch (t) {
      n.warn(`${l} Failed to load AGENTS.md from ${e}:`, t);
      return;
    }
  }
  /**
   * Orchestrates loading all context documents and applies workspace trust filtering.
   */
  async listContextDocuments() {
    try {
      const t = (await this.listSteeringFilePaths()).map((r) => a.basename(r) === C ? this.loadAgentsMdDocument(r) : this.loadSteeringDocument(r)), s = (await Promise.all(t)).filter((r) => r !== void 0);
      return this.workspaceTrusted ? s : s.filter((r) => r.scope === "global");
    } catch (e) {
      return n.warn(`${l} Error listing steering documents:`, e), [];
    }
  }
  // ---------------------------------------------------------------------------
  // Private: File matching
  // ---------------------------------------------------------------------------
  /**
   * Returns steering documents that match a specific file path based on glob patterns.
   */
  async getMatchingDocumentsForFile(e) {
    const t = await this.listContextDocuments(), s = this.findWorkspaceForFile(e);
    if (!s)
      return [];
    const r = p.file(s), o = p.file(e).path.substring(r.path.length + 1);
    return t.filter((c) => {
      if (!c.config?.fileMatchPattern || c.config.inclusion !== "fileMatch" || c.workspaceUri !== r.toString())
        return !1;
      try {
        return this.extractPattern(c.config.fileMatchPattern).some((f) => {
          const g = f.replace(/\\/g, "/");
          return I(o, g, { dot: !0 });
        });
      } catch (h) {
        return n.warn(`${l} Error matching pattern for document:`, h), !1;
      }
    });
  }
  // ---------------------------------------------------------------------------
  // Private: Helpers
  // ---------------------------------------------------------------------------
  /**
   * Finds the most specific workspace folder that contains the given file path.
   * Uses pure string prefix matching on the path.
   */
  findWorkspaceForFile(e) {
    return this.workspaceFolders.filter((t) => e.startsWith(t + a.sep) || e === t).sort((t, s) => s.length - t.length)[0];
  }
  /**
   * Normalizes a fileMatchPattern value to a string array.
   */
  extractPattern(e) {
    return e ? Array.isArray(e) ? e : [e] : [];
  }
  /**
   * Deduplicates steering documents by id.
   */
  unique(e) {
    const t = /* @__PURE__ */ new Map();
    for (const s of e)
      t.set(s.id, s);
    return Array.from(t.values());
  }
  /**
   * Recursively finds all markdown files (ending with STEERING_DOC_SUFFIX) in a directory.
   */
  async findMarkdownFilesRecursively(e) {
    try {
      const t = await d.promises.readdir(e, { withFileTypes: !0 }), s = [];
      for (const r of t) {
        const i = a.join(e, r.name);
        if (r.isFile() && r.name.endsWith(D))
          s.push(i);
        else if (r.isDirectory()) {
          const o = await this.findMarkdownFilesRecursively(i);
          s.push(...o);
        }
      }
      return s;
    } catch (t) {
      return q(t) && t.code === "ENOENT" ? [] : (n.warn(`${l} Error reading directory ${e}`, t), []);
    }
  }
  /**
   * Ensures a directory exists, creating it recursively if necessary.
   */
  async ensureDirectoryExists(e) {
    try {
      await d.promises.access(e);
    } catch {
      try {
        await d.promises.mkdir(e, { recursive: !0 }), n.info(`${l} Created directory: ${e}`);
      } catch (t) {
        n.error(`${l} Failed to create directory: ${e}`, t);
      }
    }
  }
}
function q(u) {
  return u instanceof Error && "code" in u;
}
const L = `# Architecture Selection

When the user's message starts with /architecture-selection, perform a systematic architecture analysis on the current feature's requirements.md and then proceed directly to design.

Strip the /architecture-selection prefix from the user's message. If a feature name is provided, use it; otherwise infer it from the most recently active spec in .kiro/specs/.

## Step 1: Determine Starting Point

Skip ALL entry point selection and workflow type questions. Do not ask the user whether this is a feature, bugfix, design-first, etc.

Strip the /architecture-selection prefix. Use the remainder as the feature description or feature name.

Check \`.kiro/specs/\` for an existing spec matching the feature name:

**Case A — No spec exists (new feature description provided):**
Invoke the feature-requirements-first-workflow subagent with preset "requirements" using the feature description as the prompt. Append: "SLASH_COMMAND_MODE: Do NOT use the userInput tool for any questions. Make all decisions autonomously." The subagent runs normally and shows its work in chat — it just won't pause to ask clarifying questions. Wait for requirements.md to be created, then proceed to Step 2.

**Case B — Spec exists with requirements.md (not yet designed):**
Proceed directly to Step 2 using requirements.md as input.

**Case C — Spec exists with both requirements.md and design.md:**
Proceed directly to Step 2. After writing architecture_selection.md, inform the user it has been written and suggest they update design.md to align with the selected architecture if needed. Do not re-invoke the design subagent.

**Case D — Spec exists with bugfix.md:**
Let the user know that architecture selection is designed for feature workflows and may not be meaningful for bugfixes. Ask if they'd like to proceed anyway or stop.

## Step 2: Architecture Analysis

Write your full analysis to \`.kiro/specs/{feature_name}/architecture_analysis.md\` as you work through each step below. This file is your scratchpad — it will be deleted after Step 3. Do not output analysis to the chat window.

### 2a. Variable Classification
Identify every logical variable implied by the acceptance criteria. Classify each as: Feature Flag, Configuration Parameter, Input, State, Event, Output, or Side Effect. Write as a table in architecture_analysis.md.

### 2b. Requirement–Variable Map
Build a matrix showing which variables each REQ reads (R) or writes (W). Identify clusters of variables that are frequently co-accessed — these are datatype candidates. Variables appearing across many requirements are potential coupling points. Write to architecture_analysis.md.

### 2c. Invariant Extraction
Extract invariants from the acceptance criteria (properties that must hold across all transitions). Classify each by which variables it constrains. Write to architecture_analysis.md.

### 2d. Generate 2–3 Candidate Architectures — BEFORE evaluating any of them

**CRITICAL: Fully describe ALL candidates first. Do not score or compare until all candidates are written.**

Produce genuinely different candidates (e.g. entity-oriented, use-case-oriented, layer-oriented, event-driven). Let the variable clusters guide decomposition — do not anchor to familiar patterns from the workspace. **At least one candidate must include an explicit persistence/storage boundary as a separate component.**

For each candidate, write to architecture_analysis.md:
- Component inventory (name, owned state, responsibility)
- Information flow matrix (use → for calls, ← for callbacks, ↔ for bidirectional)
- Requirement allocation matrix
- Invariant allocation matrix
- **Steelman**: describe this candidate as its strongest advocate would — what does it do exceptionally well?

If all candidates look structurally similar, you have not explored the design space enough. Generate a genuinely different one.

### 2e. Score All Candidates

Only after all candidates are fully described, compute these metrics for each:
- Cross-cutting requirements %: REQ allocated to >1 component
- Cross-cutting invariants %: invariants requiring multi-component coordination
- Information flow density: edges / (N×(N-1))
- God object score: max % of state owned by one component
- Synchronous cycles: cycles in the directed flow graph (zero is ideal)
- Max fan-in / fan-out
- Evolvability cost: avg components whose interfaces change when one REQ is added

**Red flags:** sync cycles, any component owning >50% of state, REQs allocated to 3+ components, invariants requiring global coordination.

### 2f. Select Best Candidate
Compare all candidates side by side on the metrics. Default priority: minimize cross-cutting concerns and flow density. If the user stated preferences, weight accordingly.

Selection format: "Candidate X is recommended because [metric-tied reasons]. The trade-off is [what is sacrificed]. If [alternative priority] matters more, Candidate Y would be preferable because [reasons]."

After writing architecture_selection.md in Step 3, delete architecture_analysis.md.

## Step 3: Write architecture_selection.md

Write \`.kiro/specs/{feature_name}/architecture_selection.md\` with this structure:

\`\`\`
# Architecture Selection: {feature_name}

## Recommended Architecture: {Candidate Name}

### Rationale
[2–3 sentences tied to metrics. Acknowledge the main trade-off.]

### Components
| Component | Owned State | Responsibility |
|-----------|-------------|----------------|
| ...       | ...         | ...            |

### Information Flow
| From \\ To | Comp A | Comp B | Comp C |
|------------|--------|--------|--------|
| ...        |        |        |        |

### Requirement Allocation
| Requirement | Component(s) |
|-------------|--------------|
| REQ-i.j     | ...          |

### Key Design-Induced Invariants
[Invariants that arise from this architecture's partitioning decisions, not from requirements directly]

### Alternatives Considered
| Candidate | Strength | Weakness | Why Not Selected |
|-----------|----------|----------|-----------------|
| ...       | ...      | ...      | ...             |

### Metrics Summary
| Metric | Selected | Alt A | Alt B |
|--------|----------|-------|-------|
| Cross-cutting reqs % | | | |
| Cross-cutting invariants % | | | |
| Flow density | | | |
| God object score | | | |
| Sync cycles | | | |
| Max fan-in | | | |
| Max fan-out | | | |
| Evolvability cost | | | |
\`\`\`

## Step 4: Proceed to Design

Immediately invoke the feature-requirements-first-workflow subagent with preset "design".

Append to the subagent prompt:
"ARCHITECTURE_CONTEXT: architecture_selection.md has been written. You MUST read .kiro/specs/{feature_name}/architecture_selection.md before writing the Architecture section of design.md. Use the recommended component breakdown and information flow as the basis for the Architecture section."

## After Design Is Created

Post a brief, readable summary in chat using this format:

---
**Architecture selected:** {Candidate Name}

**Components:**
- {ComponentA} — one-line responsibility
- {ComponentB} — one-line responsibility
- {ComponentC} — one-line responsibility

**Why this architecture:** [1–2 sentences on the key metric wins]
**Trade-off:** [1 sentence on what was sacrificed]

Full details in .kiro/specs/{feature_name}/architecture_selection.md
---

Then ask if the user is happy or wants to iterate.

## Iteration

If the user wants to change the architecture, re-run Steps 2–4 with their feedback. Do NOT stop between steps.

Example: "/architecture-selection my-feature"
→ read requirements.md
→ run analysis in-context
→ write architecture_selection.md
→ invoke feature-requirements-first-workflow preset "design" with ARCHITECTURE_CONTEXT appended
→ present summary`, F = [
  {
    type: "steering",
    workspaceUri: "builtin",
    id: "builtin:architecture-selection",
    displayName: "architecture-selection",
    config: { inclusion: "manual" },
    content: L,
    scope: "global",
    uri: "builtin:architecture-selection"
  },
  {
    type: "steering",
    workspaceUri: "builtin",
    id: "builtin:quick-spec",
    displayName: "quick-spec",
    config: { inclusion: "manual" },
    content: `# Quick Spec

When the user's message starts with /quick-spec, skip ALL entry point selection steps and immediately generate all 3 spec docs automatically.

Strip the /quick-spec prefix from the user's message and use the remainder as the feature description.

## Workflow

Treat the request as a new feature using the requirements-first workflow:
1. Invoke feature-requirements-first-workflow subagent with preset "requirements"
2. Immediately invoke with preset "design" — do NOT wait for user input
3. Immediately invoke with preset "tasks" — do NOT wait for user input

Append this instruction to every subagent prompt:
"SLASH_COMMAND_MODE: Do NOT use the userInput tool for any questions. Make all decisions autonomously using sensible defaults: use Structured Pseudocode if no language is detected."

## After All 3 Docs Are Created

Present a brief summary and ask if the user is happy or wants to iterate.

## Iteration

When the user wants to change something (e.g. "make it simple"), update ALL 3 documents by invoking each phase sequentially with the user's feedback as the prompt (with SLASH_COMMAND_MODE appended). Do NOT stop between phases.

Example: "/quick-spec create a calculator app"
→ invoke feature-requirements-first-workflow preset "requirements" with prompt "create a calculator app. SLASH_COMMAND_MODE: ..."
→ immediately invoke preset "design"
→ immediately invoke preset "tasks"
→ present summary, ask if happy or want changes`,
    scope: "global",
    uri: "builtin:quick-spec"
  }
];
class G {
  constructor(e) {
    this.inner = e;
  }
  /** Returns always-included documents from inner source merged with built-in always docs. */
  async getAlwaysIncludedDocuments() {
    const e = await this.inner.getAlwaysIncludedDocuments(), t = F.filter(
      (s) => s.config?.inclusion === "always" || !s.config?.inclusion
    );
    return [...e, ...t];
  }
  /** Delegates file-matched document lookup to the inner source. */
  async getMatchedDocuments(e) {
    return this.inner.getMatchedDocuments(e);
  }
  /** Returns all built-in steering documents. */
  getAllDocuments() {
    return F;
  }
}
const N = "_kiro/steering/documents_changed", U = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "out"]);
class J {
  source;
  emitExtNotification;
  sessions = /* @__PURE__ */ new Map();
  fileWatcher;
  disposed = !1;
  /** Fire-and-forget a promise, logging failures at warn level. */
  logOnError(e, t) {
    e.catch((s) => {
      const r = s instanceof Error ? s.message : String(s);
      n.warn(`[SteeringManager] ${t}: ${r}`);
    });
  }
  constructor(e) {
    this.source = new G(
      new W({
        workspaceFolders: e.workspaceFolders,
        homeDir: e.homeDir,
        workspaceTrusted: e.workspaceTrusted
      })
    ), this.emitExtNotification = e.emitExtNotification, this.fileWatcher = new T({
      subPaths: ["steering"],
      homeDir: e.homeDir,
      watchHome: !0,
      watchWorkspaces: !0,
      fileFilter: (t) => t.includes(a.join(".kiro", "steering")) || a.basename(t) === "AGENTS.md",
      extraWatchPaths: (t) => [t],
      chokidarOptions: {
        ignored: (t) => {
          const s = a.basename(t);
          return U.has(s);
        }
      },
      onChange: () => {
        this.logOnError(this.pushToAllSessions(), "Failed debounced push");
      }
    }), e.workspaceFolders.length > 0 && this.fileWatcher.addWorkspacePaths(e.workspaceFolders);
  }
  /**
   * Register a new session. Adds workspace watchers for any new paths,
   * immediately pushes current steering documents, and returns an
   * ISteeringProvider for the session that reads from the cached docs.
   */
  onSessionCreated(e, t) {
    const s = { documents: [], workspacePaths: t };
    this.disposed || (t.length > 0 && this.fileWatcher.addWorkspacePaths(t), this.sessions.set(e, s), n.debug(`[SteeringManager] Session created: ${e}`), this.logOnError(this.pushToSession(e), `Failed initial push to session ${e}`));
    const r = this.source;
    return {
      async getDocuments(i) {
        if (!i || i.length === 0)
          return s.documents;
        const o = i.map((h) => h.fsPath), c = await r.getMatchedDocuments(o);
        return j([...s.documents, ...c]);
      }
    };
  }
  /**
   * Unregister a session.
   * Note: watched paths are NOT removed (chokidar unwatch is not ref-counted).
   * Paths accumulate harmlessly and are cleaned up on dispose().
   */
  onSessionDestroyed(e) {
    this.sessions.delete(e), n.debug(`[SteeringManager] Session destroyed: ${e}`);
  }
  /**
   * Clean up the watcher, timers, and session tracking.
   */
  dispose() {
    this.disposed = !0, this.fileWatcher.dispose(), this.sessions.clear(), n.debug("[SteeringManager] Disposed");
  }
  /**
   * Push steering documents to all active sessions.
   */
  async pushToAllSessions() {
    if (!this.disposed)
      try {
        const e = await this.source.getAlwaysIncludedDocuments();
        if (this.disposed)
          return;
        n.debug(`[SteeringManager] Pushing ${e.length} docs to ${this.sessions.size} sessions`);
        for (const [t, s] of this.sessions)
          try {
            s.documents = e, this.logOnError(
              this.emitDocumentsChanged(t, e),
              `Failed to emit notification for session ${t}`
            );
          } catch (r) {
            const i = r instanceof Error ? r.message : String(r);
            n.warn(`[SteeringManager] Failed to push docs to session ${t}: ${i}`), this.logOnError(
              this.emitDocumentsFailed(t, i),
              `Failed to emit failure notification for session ${t}`
            );
          }
      } catch (e) {
        const t = e instanceof Error ? e.message : String(e);
        n.warn(`[SteeringManager] Failed to read steering documents: ${t}`);
        for (const s of this.sessions.keys())
          this.logOnError(
            this.emitDocumentsFailed(s, t),
            `Failed to emit failure notification for session ${s}`
          );
      }
  }
  /**
   * Push steering documents to a single session.
   */
  async pushToSession(e) {
    if (!this.disposed)
      try {
        const t = await this.source.getAlwaysIncludedDocuments(), s = this.sessions.get(e);
        s && (s.documents = t), n.debug(`[SteeringManager] Pushed ${t.length} docs to session ${e}`), this.logOnError(
          this.emitDocumentsChanged(e, t),
          `Failed to emit notification for session ${e}`
        );
      } catch (t) {
        const s = t instanceof Error ? t.message : String(t);
        n.warn(`[SteeringManager] Failed to push docs to session ${e}: ${s}`), this.logOnError(
          this.emitDocumentsFailed(e, s),
          `Failed to emit failure notification for session ${e}`
        );
      }
  }
  /** Emit a success notification to the client. */
  async emitDocumentsChanged(e, t) {
    const s = t.map((i) => ({
      name: i.displayName,
      type: i.source ?? "steering"
    })), r = {
      sessionId: e,
      status: "success",
      documents: s
    };
    await this.emitExtNotification(N, r);
  }
  /** Emit a failure notification to the client. */
  async emitDocumentsFailed(e, t) {
    const s = {
      sessionId: e,
      status: "failed",
      error: t
    };
    await this.emitExtNotification(N, s);
  }
}
function j(u) {
  const e = /* @__PURE__ */ new Map();
  for (const t of u)
    e.set(t.id, t);
  return Array.from(e.values());
}
const H = 300, A = "_kiro/progressive_context/items_changed";
class Z {
  source;
  emitExtNotification;
  homeDir;
  sessions = /* @__PURE__ */ new Map();
  fileWatcher;
  cachedItems = /* @__PURE__ */ new Map();
  debounceTimer;
  disposed = !1;
  /** Fire-and-forget a promise, logging failures at warn level. */
  logOnError(e, t) {
    e.catch((s) => {
      const r = s instanceof Error ? s.message : String(s);
      n.warn(`[ProgressiveContextManager] ${t}: ${r}`);
    });
  }
  constructor(e) {
    this.source = e.source, this.emitExtNotification = e.emitExtNotification, this.homeDir = e.homeDir;
  }
  /**
   * Register a new session. Installs watchers as needed.
   * Triggers an initial scan regardless of whether watchers are available.
   */
  onSessionCreated(e, t) {
    this.disposed || (this.sessions.set(e, { workspacePaths: t }), n.debug(`[ProgressiveContextManager] Session created: ${e}`), this.fileWatcher || (this.fileWatcher = new T({
      subPaths: ["skills", "steering"],
      homeDir: this.homeDir,
      watchHome: !0,
      watchWorkspaces: !0,
      onChange: () => {
        this.disposed || this.debouncedScanAll();
      }
    })), this.fileWatcher.addWorkspacePaths(t), this.debouncedScanAll());
  }
  /**
   * Unregister a session.
   * Note: watched paths are NOT removed (chokidar unwatch is not ref-counted).
   * Paths accumulate harmlessly and are cleaned up on dispose().
   */
  onSessionDestroyed(e) {
    this.sessions.delete(e), n.debug(`[ProgressiveContextManager] Session destroyed: ${e}`);
  }
  /**
   * Returns the cached list of progressive context items (deduplicated).
   */
  getItems() {
    return [...this.cachedItems.values()];
  }
  /**
   * Retrieves the content for a specific progressive context item by name.
   * Delegates to the injected source for actual file reading.
   */
  async getContent(e) {
    const t = this.cachedItems.get(e);
    if (!t)
      return;
    const s = await this.source.getContent(t);
    if (s !== void 0)
      return { content: s, item: t };
  }
  /**
   * Clean up the watcher, timers, and session tracking.
   */
  dispose() {
    this.disposed = !0, this.debounceTimer !== void 0 && (clearTimeout(this.debounceTimer), this.debounceTimer = void 0), this.fileWatcher && (this.fileWatcher.dispose(), this.fileWatcher = void 0), this.sessions.clear(), this.cachedItems.clear(), n.debug("[ProgressiveContextManager] Disposed");
  }
  /**
   * Debounce scan events before scanning all sessions.
   */
  debouncedScanAll() {
    this.debounceTimer !== void 0 && clearTimeout(this.debounceTimer), this.debounceTimer = setTimeout(() => {
      this.debounceTimer = void 0, this.logOnError(this.scanAndNotifyAll(), "Failed debounced scan");
    }, H);
  }
  /**
   * Scan all known directories for progressive context items and notify sessions.
   */
  async scanAndNotifyAll() {
    if (!this.disposed)
      try {
        const e = this.getAllWorkspacePaths(), t = await this.source.scanAll(e);
        if (this.disposed)
          return;
        const s = /* @__PURE__ */ new Map();
        for (const r of t)
          s.set(r.displayName, r);
        this.cachedItems = s, n.debug(
          `[ProgressiveContextManager] Scanned ${t.length} items, ${s.size} unique, for ${this.sessions.size} sessions`
        );
        for (const r of this.sessions.keys())
          this.logOnError(this.emitItemsChanged(r), `Failed to emit notification for session ${r}`);
      } catch (e) {
        const t = e instanceof Error ? e.message : String(e);
        n.warn(`[ProgressiveContextManager] Failed to scan progressive context items: ${t}`);
        for (const s of this.sessions.keys())
          this.logOnError(
            this.emitItemsFailed(s, t),
            `Failed to emit failure notification for session ${s}`
          );
      }
  }
  getAllWorkspacePaths() {
    const e = /* @__PURE__ */ new Set();
    for (const t of this.sessions.values())
      for (const s of t.workspacePaths)
        e.add(s);
    return [...e];
  }
  async emitItemsChanged(e) {
    const s = this.getItems().map((i) => ({
      name: i.displayName,
      type: _(i) ? "skill" : "steering",
      description: i.config?.description
    })), r = {
      sessionId: e,
      status: "success",
      items: s
    };
    await this.emitExtNotification(A, r);
  }
  async emitItemsFailed(e, t) {
    const s = {
      sessionId: e,
      status: "failed",
      error: t
    };
    await this.emitExtNotification(A, s);
  }
}
class ee {
  ensuredGlobalDir = !1;
  /**
   * Scans all global and workspace directories for progressive context items.
   * Global items are returned first, workspace items after, so that consumers
   * can implement last-wins deduplication (workspace overrides global).
   */
  async scanAll(e) {
    this.ensuredGlobalDir || (await this.ensureDirectoryExists(a.join(M.homedir(), y, S)), this.ensuredGlobalDir = !0);
    const t = [], s = [], r = M.homedir(), i = a.join(r, y, S), o = a.join(r, y, w), c = await this.scanSkillsDirectory(i, "global", i), h = await this.scanSteeringDirectory(o, "global", o);
    t.push(...c), s.push(...h);
    for (const f of e) {
      const g = a.join(f, y, S), m = a.join(f, y, w), E = await this.scanSkillsDirectory(g, "workspace", f);
      t.push(...E);
      const $ = await this.scanSteeringDirectory(m, "workspace", f);
      s.push(...$);
    }
    return [...t, ...s];
  }
  /**
   * Reads content for a specific item from disk, stripping frontmatter.
   */
  async getContent(e) {
    try {
      const t = await d.promises.readFile(e.uri, "utf-8");
      return R(t);
    } catch (t) {
      const s = t instanceof Error ? t.message : String(t);
      n.warn(`[NodeProgressiveContextSource] Failed to read content for "${e.displayName}": ${s}`);
      return;
    }
  }
  // Skills scanning
  async scanSkillsDirectory(e, t, s) {
    const r = [];
    try {
      const i = await d.promises.readdir(e, { withFileTypes: !0 });
      for (const o of i) {
        if (!o.isDirectory())
          continue;
        const c = a.join(e, o.name, P), h = await this.parseSkillMd(c, t, s);
        h && r.push(h);
      }
    } catch (i) {
      if (b(i) && i.code === "ENOENT")
        return [];
      n.warn(`[NodeProgressiveContextSource] Skills directory not accessible: ${e}`, i);
    }
    return r;
  }
  async parseSkillMd(e, t, s) {
    try {
      const r = await d.promises.readFile(e, "utf-8"), i = k({ schema: O, content: r });
      if (!i.frontMatter) {
        n.warn(`[NodeProgressiveContextSource] SKILL.md missing frontmatter: ${e}`);
        return;
      }
      if (!i.frontMatter.name || !i.frontMatter.description) {
        n.warn(`[NodeProgressiveContextSource] SKILL.md missing name or description: ${e}`);
        return;
      }
      return {
        type: "skill",
        workspaceUri: s,
        id: e,
        displayName: i.frontMatter.name,
        config: i.frontMatter,
        scope: t,
        uri: e
      };
    } catch (r) {
      b(r) && r.code === "ENOENT" ? n.warn(`[NodeProgressiveContextSource] SKILL.md not found: ${e}`) : r instanceof v ? n.warn(`[NodeProgressiveContextSource] Invalid SKILL.md frontmatter: ${e}`, r) : n.warn(`[NodeProgressiveContextSource] Error reading SKILL.md: ${e}`, r);
      return;
    }
  }
  // Steering scanning
  async scanSteeringDirectory(e, t, s) {
    const r = [], i = await this.findMarkdownFilesRecursively(e);
    for (const o of i) {
      const c = await this.parseProgressiveSteeringFile(o, t, s);
      c && r.push(c);
    }
    return r;
  }
  async parseProgressiveSteeringFile(e, t, s) {
    try {
      const r = await d.promises.readFile(e, "utf-8"), i = k({ schema: x, content: r });
      if (!i.frontMatter || i.frontMatter.inclusion !== "auto")
        return;
      if (!i.frontMatter.description) {
        n.warn(`[NodeProgressiveContextSource] Progressive steering file missing description: ${e}`);
        return;
      }
      const o = i.frontMatter.name || a.basename(e, D);
      return {
        type: "steering",
        workspaceUri: s,
        id: e,
        displayName: o,
        config: i.frontMatter,
        content: i.content,
        scope: t,
        uri: e
      };
    } catch (r) {
      r instanceof v ? n.warn(`[NodeProgressiveContextSource] Invalid steering frontmatter: ${e}`, r) : n.warn(`[NodeProgressiveContextSource] Error reading steering file: ${e}`, r);
      return;
    }
  }
  // Filesystem helpers
  async findMarkdownFilesRecursively(e) {
    try {
      const t = await d.promises.readdir(e, { withFileTypes: !0 }), s = [];
      for (const r of t) {
        const i = a.join(e, r.name);
        if (r.isFile() && r.name.endsWith(D))
          s.push(i);
        else if (r.isDirectory()) {
          const o = await this.findMarkdownFilesRecursively(i);
          s.push(...o);
        }
      }
      return s;
    } catch (t) {
      return b(t) && t.code === "ENOENT" ? [] : (n.warn(`[NodeProgressiveContextSource] Error reading directory ${e}`, t), []);
    }
  }
  async ensureDirectoryExists(e) {
    try {
      await d.promises.access(e);
    } catch {
      try {
        await d.promises.mkdir(e, { recursive: !0 }), n.info(`[NodeProgressiveContextSource] Created directory: ${e}`);
      } catch (t) {
        n.error(`[NodeProgressiveContextSource] Failed to create directory: ${e}`, t);
      }
    }
  }
}
function b(u) {
  return u instanceof Error && "code" in u;
}
export {
  F as B,
  ee as N,
  Z as P,
  J as S,
  G as a,
  W as b
};
//# sourceMappingURL=node-progressive-context-source-DZE-KhrH.js.map
