import * as a from "path";
import { C } from "./config-file-watcher-DbLp_qKw.js";
import { a as o } from "./logger-CTb8_yz9.js";
import * as d from "fs";
import { URI as f } from "vscode-uri";
import { minimatch as A } from "minimatch";
import { a as T, i as P, S as I } from "./steering-Dcn_tjkT.js";
import { p as F, s as O, F as M } from "./parse-front-matter-CXai4UYs.js";
import { P as w, S as y, A as $, a as E, b as S, c as R } from "./config-constants-CsFJbjLP.js";
import * as v 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, w, y)
    ), this.globalSteeringDir = a.join(this.homeDir, w, y);
  }
  /**
   * 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, $);
      try {
        return await d.promises.access(t), o.debug(`${l} Found AGENTS.md file in ${e}`), [t];
      } catch {
        return o.debug(`${l} No AGENTS.md file found in ${e}`), [];
      }
    } catch (t) {
      return o.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), o.debug(`${l} Found ${t.length} global steering files`);
      } catch (r) {
        o.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 o.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 g = f.file(this.globalSteeringDir);
        s = f.file(e).path.substring(g.path.length + 1).replace(/\.md$/, ""), r = "global";
      } else {
        const g = this.findWorkspaceForFile(e);
        if (!g) {
          o.warn(`${l} File ${e} is not in any workspace`);
          return;
        }
        t = g, s = a.basename(e, ".md"), r = "workspace";
      }
      const n = await d.promises.readFile(e, "utf-8"), c = f.file(e).toString(), h = f.file(t).toString();
      let m, p;
      try {
        const g = F({ schema: T, content: n });
        m = g.frontMatter ? {
          fileMatchPattern: this.extractPattern(g.frontMatter.fileMatchPattern ?? void 0),
          inclusion: g.frontMatter.inclusion
        } : void 0, p = g.content;
      } catch {
        m = void 0, p = n;
      }
      return {
        type: "steering",
        workspaceUri: h,
        id: c,
        displayName: s,
        config: m,
        content: p,
        source: "steering",
        scope: r,
        uri: c
      };
    } catch (t) {
      o.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) {
        o.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), n = f.file(e).toString();
      return {
        type: "steering",
        workspaceUri: f.file(t).toString(),
        id: n,
        displayName: i,
        config: {
          inclusion: "always",
          fileMatchPattern: void 0
        },
        content: s,
        source: "agents-md",
        scope: "workspace",
        // AGENTS.md files are always workspace-scoped
        uri: n
      };
    } catch (t) {
      o.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) === $ ? 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 o.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 = f.file(s), n = f.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((m) => {
          const p = m.replace(/\\/g, "/");
          return A(n, p, { dot: !0 });
        });
      } catch (h) {
        return o.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(E))
          s.push(i);
        else if (r.isDirectory()) {
          const n = await this.findMarkdownFilesRecursively(i);
          s.push(...n);
        }
      }
      return s;
    } catch (t) {
      return _(t) && t.code === "ENOENT" ? [] : (o.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 }), o.info(`${l} Created directory: ${e}`);
      } catch (t) {
        o.error(`${l} Failed to create directory: ${e}`, t);
      }
    }
  }
}
function _(u) {
  return u instanceof Error && "code" in u;
}
const N = "_kiro/steering/documents_changed", G = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "out"]);
class B {
  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);
      o.warn(`[SteeringManager] ${t}: ${r}`);
    });
  }
  constructor(e) {
    this.source = new W({
      workspaceFolders: e.workspaceFolders,
      homeDir: e.homeDir,
      workspaceTrusted: e.workspaceTrusted
    }), this.emitExtNotification = e.emitExtNotification, this.fileWatcher = new C({
      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 G.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), o.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 n = i.map((h) => h.fsPath), c = await r.getMatchedDocuments(n);
        return L([...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), o.debug(`[SteeringManager] Session destroyed: ${e}`);
  }
  /**
   * Clean up the watcher, timers, and session tracking.
   */
  dispose() {
    this.disposed = !0, this.fileWatcher.dispose(), this.sessions.clear(), o.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;
        o.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);
            o.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);
        o.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), o.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);
        o.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 L(u) {
  const e = /* @__PURE__ */ new Map();
  for (const t of u)
    e.set(t.id, t);
  return Array.from(e.values());
}
const U = 300, b = "_kiro/progressive_context/items_changed";
class V {
  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);
      o.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 }), o.debug(`[ProgressiveContextManager] Session created: ${e}`), this.fileWatcher || (this.fileWatcher = new C({
      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), o.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(), o.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");
    }, U);
  }
  /**
   * 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, o.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);
        o.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: P(i) ? "skill" : "steering",
      description: i.config?.description
    })), r = {
      sessionId: e,
      status: "success",
      items: s
    };
    await this.emitExtNotification(b, r);
  }
  async emitItemsFailed(e, t) {
    const s = {
      sessionId: e,
      status: "failed",
      error: t
    };
    await this.emitExtNotification(b, s);
  }
}
class J {
  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(v.homedir(), w, S)), this.ensuredGlobalDir = !0);
    const t = [], s = [], r = v.homedir(), i = a.join(r, w, S), n = a.join(r, w, y), c = await this.scanSkillsDirectory(i, "global", i), h = await this.scanSteeringDirectory(n, "global", n);
    t.push(...c), s.push(...h);
    for (const m of e) {
      const p = a.join(m, w, S), g = a.join(m, w, y), k = await this.scanSkillsDirectory(p, "workspace", m);
      t.push(...k);
      const x = await this.scanSteeringDirectory(g, "workspace", m);
      s.push(...x);
    }
    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 O(t);
    } catch (t) {
      const s = t instanceof Error ? t.message : String(t);
      o.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 n of i) {
        if (!n.isDirectory())
          continue;
        const c = a.join(e, n.name, R), h = await this.parseSkillMd(c, t, s);
        h && r.push(h);
      }
    } catch (i) {
      if (D(i) && i.code === "ENOENT")
        return [];
      o.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 = F({ schema: I, content: r });
      if (!i.frontMatter) {
        o.warn(`[NodeProgressiveContextSource] SKILL.md missing frontmatter: ${e}`);
        return;
      }
      if (!i.frontMatter.name || !i.frontMatter.description) {
        o.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) {
      D(r) && r.code === "ENOENT" ? o.warn(`[NodeProgressiveContextSource] SKILL.md not found: ${e}`) : r instanceof M ? o.warn(`[NodeProgressiveContextSource] Invalid SKILL.md frontmatter: ${e}`, r) : o.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 n of i) {
      const c = await this.parseProgressiveSteeringFile(n, t, s);
      c && r.push(c);
    }
    return r;
  }
  async parseProgressiveSteeringFile(e, t, s) {
    try {
      const r = await d.promises.readFile(e, "utf-8"), i = F({ schema: T, content: r });
      if (!i.frontMatter || i.frontMatter.inclusion !== "auto")
        return;
      if (!i.frontMatter.description) {
        o.warn(`[NodeProgressiveContextSource] Progressive steering file missing description: ${e}`);
        return;
      }
      const n = i.frontMatter.name || a.basename(e, E);
      return {
        type: "steering",
        workspaceUri: s,
        id: e,
        displayName: n,
        config: i.frontMatter,
        content: i.content,
        scope: t,
        uri: e
      };
    } catch (r) {
      r instanceof M ? o.warn(`[NodeProgressiveContextSource] Invalid steering frontmatter: ${e}`, r) : o.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(E))
          s.push(i);
        else if (r.isDirectory()) {
          const n = await this.findMarkdownFilesRecursively(i);
          s.push(...n);
        }
      }
      return s;
    } catch (t) {
      return D(t) && t.code === "ENOENT" ? [] : (o.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 }), o.info(`[NodeProgressiveContextSource] Created directory: ${e}`);
      } catch (t) {
        o.error(`[NodeProgressiveContextSource] Failed to create directory: ${e}`, t);
      }
    }
  }
}
function D(u) {
  return u instanceof Error && "code" in u;
}
export {
  J as N,
  V as P,
  B as S,
  W as a
};
//# sourceMappingURL=node-progressive-context-source-C3Yk_3xF.js.map
