@mulmoclaude/core 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,9 +4,9 @@ const require_dist = require("../../../dist-CsgSfWwR.cjs");
4
4
  const require_root = require("../../../root-rPH6FGDT.cjs");
5
5
  const require_relPath = require("../../../relPath-CTAjGdCL.cjs");
6
6
  const require_utils_fetch = require("../../../utils/fetch.cjs");
7
- const require_discovery = require("../../../discovery-CDOjfctD.cjs");
7
+ const require_discovery = require("../../../discovery-CrA_EqUJ.cjs");
8
8
  const require_templatePath = require("../../../templatePath-27vUZowm.cjs");
9
- const require_server = require("../../../server-WU35UYlF.cjs");
9
+ const require_server = require("../../../server-CorfPQVQ.cjs");
10
10
  const require_skill_bridge_index = require("../../../skill-bridge/index.cjs");
11
11
  const require_types = require("../../../types-CNqkLT4M.cjs");
12
12
  let node_fs = require("node:fs");
@@ -2,9 +2,9 @@ import { t as errorMessage } from "../../../dist-D8zokgGo.js";
2
2
  import { n as writeFileAtomic } from "../../../root-BMroU_mB.js";
3
3
  import { n as toPosixRelPath } from "../../../relPath-DW8MC8VO.js";
4
4
  import { fetchWithTimeout } from "../../../utils/fetch.js";
5
- import { $ as projectSkillsDir, H as safeRecordId, Y as getWorkspaceRoot, Z as log, nt as skillsStagingDir, q as collectionsRegistriesConfigPath, r as loadCollection, s as CollectionSchemaZ, t as acceptParsedSchema } from "../../../discovery-CC7kuwHf.js";
5
+ import { $ as projectSkillsDir, H as safeRecordId, Y as getWorkspaceRoot, Z as log, nt as skillsStagingDir, q as collectionsRegistriesConfigPath, r as loadCollection, s as CollectionSchemaZ, t as acceptParsedSchema } from "../../../discovery-D7LCN8gJ.js";
6
6
  import { t as isSafeActionTemplatePath } from "../../../templatePath-k_WNbL_Q.js";
7
- import { g as ONE_SECOND_MS } from "../../../server-AA2kfDfk.js";
7
+ import { g as ONE_SECOND_MS } from "../../../server-D6QY853r.js";
8
8
  import { claudeSkillDir, dataSkillDir, mirrorSkillWrite } from "../../../skill-bridge/index.js";
9
9
  import { n as parseRegistryIndex, r as isRecord, t as OFFICIAL_REGISTRY_NAME } from "../../../types-BKVZrtyc.js";
10
10
  import { readFileSync } from "node:fs";
@@ -52,8 +52,16 @@ export interface DiscoveryOptions {
52
52
  /** Override `~/.claude/skills/` for tests. Production callers
53
53
  * leave this unset. Without an override, even a test-scoped
54
54
  * workspaceRoot still scans the real user home — which can leak
55
- * unrelated skills into the result. */
56
- userSkillsDir?: string | undefined;
55
+ * unrelated skills into the result.
56
+ *
57
+ * Three distinct values, and the difference matters — a caller that
58
+ * thinks it opted out and did not is exactly the failure the scope
59
+ * isolation removes:
60
+ * - `undefined` (or absent): ask the host binding for this root, which
61
+ * may itself answer `null`.
62
+ * - a path: scan that dir as user scope.
63
+ * - `null`: this call has NO user scope. The host is not consulted. */
64
+ userSkillsDir?: string | null | undefined;
57
65
  }
58
66
  /** Discover every schema-driven collection available to this
59
67
  * workspace. Project-scope collections override user-scope on slug
@@ -40,8 +40,29 @@ export interface CollectionHost {
40
40
  /** Host workspace layout — supplied as the host's own path helpers so the
41
41
  * package owns no layout literals and works against a test/alt root. */
42
42
  paths: {
43
- /** Absolute user-scope skills dir (host-specific, e.g. `~/.claude/skills`). */
44
- userSkillsDir: string;
43
+ /** Absolute user-scope skills dir for a root (host-specific, e.g.
44
+ * `~/.claude/skills`), or `null` when this root has NO user scope.
45
+ *
46
+ * A single-workspace host (MulmoClaude) returns the same path for its one
47
+ * root and behaves exactly as before — user scope merges into the
48
+ * workspace and a project slug still shadows a user one.
49
+ *
50
+ * A multi-root host returns `null` for a plain project directory: `~` and
51
+ * a project are separate worlds, and a project that could resolve a
52
+ * machine-global collection would depend on something no clone of it can
53
+ * have. Under `null` the user pass is skipped in BOTH discovery and
54
+ * `loadCollection` — a user-only slug is then a miss, not a quiet hop
55
+ * into another world. Resolution is where the guarantee has to hold:
56
+ * filtering only the listing would still let a slug typed by the agent,
57
+ * or arriving in a URL, write into `~/.claude/skills` from a project.
58
+ *
59
+ * A bare `string` is the pre-3.3.0 form and still works, read as "this
60
+ * path, for every root". It is accepted rather than required-away because
61
+ * a caret range floats across minors: a host pinned at `^3.2.0` installs
62
+ * this version without touching its code, and a required callable would
63
+ * turn that into a TypeError on its first discovery — a crash, not an
64
+ * opt-out. Prefer the function form; the string is deprecated. */
65
+ userSkillsDir: string | ((workspaceRoot: string) => string | null);
45
66
  /** Absolute project-scope skills dir for a workspace (`<root>/.claude/skills`). */
46
67
  projectSkillsDir: (workspaceRoot: string) => string;
47
68
  /** Absolute feeds-registry root for a workspace (`<root>/data/feeds`). */
@@ -123,7 +144,10 @@ export declare function getWorkspaceRoot(): string;
123
144
  * operate on one. Anything that will touch the filesystem wants
124
145
  * `getWorkspaceRoot()` and its loud failure instead. */
125
146
  export declare function peekWorkspaceRoot(): string | null;
126
- export declare function userSkillsDir(): string;
147
+ /** The user-scope skills dir for a root, or `null` when it has none. The one
148
+ * place the pre-3.3.0 `string` binding is normalized, so nothing downstream
149
+ * has to know the host might not have upgraded its shape yet. */
150
+ export declare function userSkillsDir(workspaceRoot: string): string | null;
127
151
  export declare function projectSkillsDir(workspaceRoot: string): string;
128
152
  export declare function feedsRoot(workspaceRoot: string): string;
129
153
  export declare function skillsStagingDir(workspaceRoot: string): string | null;
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_root = require("../../root-rPH6FGDT.cjs");
3
3
  const require_calendarGrid = require("../../calendarGrid-p8K-7cuB.cjs");
4
- const require_discovery = require("../../discovery-CDOjfctD.cjs");
4
+ const require_discovery = require("../../discovery-CrA_EqUJ.cjs");
5
5
  const require_templatePath = require("../../templatePath-27vUZowm.cjs");
6
- const require_server = require("../../server-WU35UYlF.cjs");
6
+ const require_server = require("../../server-CorfPQVQ.cjs");
7
7
  exports.BackendUnavailableError = require_discovery.BackendUnavailableError;
8
8
  exports.COLLECTION_ROOT_REQUIRED = require_discovery.COLLECTION_ROOT_REQUIRED;
9
9
  exports.COMPUTED_TYPES = require_calendarGrid.COMPUTED_TYPES;
@@ -1,6 +1,6 @@
1
1
  import { t as canonicalRoot } from "../../root-BMroU_mB.js";
2
2
  import { S as COMPUTED_TYPES } from "../../calendarGrid-Csy2rpjp.js";
3
- import { A as deleteItem, B as resolveDataDir, C as compileCsvQuery, D as MAX_QUERY_ROWS, E as DEFAULT_QUERY_ROWS, F as resolveCreateItemId, H as safeRecordId, I as writeItem, J as configureCollectionHost, K as collectionChangePayload, L as SCHEMA_FILE, M as isRegularFile, N as listItems, O as BackendUnavailableError, P as readItem, Q as peekWorkspaceRoot, R as isContainedInRoot, T as CollectionQueryZ, U as safeSlugName, V as resolveTemplatePath, W as COLLECTION_ROOT_REQUIRED, Y as getWorkspaceRoot, Z as log, _ as csvRowToItem, a as toDetail, b as encodeCsvRecordId, d as storeFor, et as publishCollectionChange, h as MAX_CSV_ROWS, i as resolvePrimaryField, j as generateItemId, k as isBackendUnavailable, l as collectionWritable, m as projectItemFields, n as discoverCollections, o as toSummary, p as pageFromFullRead, r as loadCollection, s as CollectionSchemaZ, t as acceptParsedSchema, tt as setCollectionChangePublisher, u as readOnlyRefusal, v as decodeCsvRecordId, w as compileJsonlQuery, x as normalizeCsvValue, y as dedupeByRecordId, z as itemFilePath } from "../../discovery-CC7kuwHf.js";
3
+ import { A as deleteItem, B as resolveDataDir, C as compileCsvQuery, D as MAX_QUERY_ROWS, E as DEFAULT_QUERY_ROWS, F as resolveCreateItemId, H as safeRecordId, I as writeItem, J as configureCollectionHost, K as collectionChangePayload, L as SCHEMA_FILE, M as isRegularFile, N as listItems, O as BackendUnavailableError, P as readItem, Q as peekWorkspaceRoot, R as isContainedInRoot, T as CollectionQueryZ, U as safeSlugName, V as resolveTemplatePath, W as COLLECTION_ROOT_REQUIRED, Y as getWorkspaceRoot, Z as log, _ as csvRowToItem, a as toDetail, b as encodeCsvRecordId, d as storeFor, et as publishCollectionChange, h as MAX_CSV_ROWS, i as resolvePrimaryField, j as generateItemId, k as isBackendUnavailable, l as collectionWritable, m as projectItemFields, n as discoverCollections, o as toSummary, p as pageFromFullRead, r as loadCollection, s as CollectionSchemaZ, t as acceptParsedSchema, tt as setCollectionChangePublisher, u as readOnlyRefusal, v as decodeCsvRecordId, w as compileJsonlQuery, x as normalizeCsvValue, y as dedupeByRecordId, z as itemFilePath } from "../../discovery-D7LCN8gJ.js";
4
4
  import { i as isSafeTemplatePath, n as isSafeCustomViewI18nPath, r as isSafeCustomViewPath, t as isSafeActionTemplatePath } from "../../templatePath-k_WNbL_Q.js";
5
- import { A as buildCollectionActionSeedPrompt, C as validateRecordObject, D as enrichItems, E as runCollectionQuery, M as readCustomViewHtml, N as readCustomViewI18n, O as runQueryOverRows, P as readSkillTemplate, S as validateCollectionRecords, T as recordFieldProblem, _ as computeCollectionIcon, a as deleteCollection, b as applyMutateAction, c as computeSuccessor, d as isTriggerDue, f as maybeSpawnSuccessor, h as successorId, i as deleteCustomView, j as promptPathsFor, k as buildActionSeedPrompt, l as daysInMonth, m as resolveEvery, n as MAX_UNSELECTIVE_ITEMS, o as deleteCollectionRefusalMessage, p as parseCivil, r as makeManageCollectionTool, s as advanceTriggerDate, t as MAX_SCHEMA_ISSUES, u as formatCivil, v as buildWorkspaceOntology, w as compileRecordZ, x as firstMutateParamProblem, y as schemaRelations } from "../../server-AA2kfDfk.js";
5
+ import { A as buildCollectionActionSeedPrompt, C as validateRecordObject, D as enrichItems, E as runCollectionQuery, M as readCustomViewHtml, N as readCustomViewI18n, O as runQueryOverRows, P as readSkillTemplate, S as validateCollectionRecords, T as recordFieldProblem, _ as computeCollectionIcon, a as deleteCollection, b as applyMutateAction, c as computeSuccessor, d as isTriggerDue, f as maybeSpawnSuccessor, h as successorId, i as deleteCustomView, j as promptPathsFor, k as buildActionSeedPrompt, l as daysInMonth, m as resolveEvery, n as MAX_UNSELECTIVE_ITEMS, o as deleteCollectionRefusalMessage, p as parseCivil, r as makeManageCollectionTool, s as advanceTriggerDate, t as MAX_SCHEMA_ISSUES, u as formatCivil, v as buildWorkspaceOntology, w as compileRecordZ, x as firstMutateParamProblem, y as schemaRelations } from "../../server-D6QY853r.js";
6
6
  export { BackendUnavailableError, COLLECTION_ROOT_REQUIRED, COMPUTED_TYPES, CollectionQueryZ, CollectionSchemaZ, DEFAULT_QUERY_ROWS, MAX_CSV_ROWS, MAX_QUERY_ROWS, MAX_SCHEMA_ISSUES, MAX_UNSELECTIVE_ITEMS, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, applyMutateAction, buildActionSeedPrompt, buildCollectionActionSeedPrompt, buildWorkspaceOntology, canonicalRoot, collectionChangePayload, collectionWritable, compileCsvQuery, compileJsonlQuery, compileRecordZ, computeCollectionIcon, computeSuccessor, configureCollectionHost, csvRowToItem, daysInMonth, decodeCsvRecordId, dedupeByRecordId, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, encodeCsvRecordId, enrichItems, firstMutateParamProblem, formatCivil, generateItemId, getWorkspaceRoot, isBackendUnavailable, isContainedInRoot, isRegularFile, isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, log, makeManageCollectionTool, maybeSpawnSuccessor, normalizeCsvValue, pageFromFullRead, parseCivil, peekWorkspaceRoot, projectItemFields, promptPathsFor, publishCollectionChange, readCustomViewHtml, readCustomViewI18n, readItem, readOnlyRefusal, readSkillTemplate, recordFieldProblem, resolveCreateItemId, resolveDataDir, resolveEvery, resolvePrimaryField, resolveTemplatePath, runCollectionQuery, runQueryOverRows, safeRecordId, safeSlugName, schemaRelations, setCollectionChangePublisher, storeFor, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
@@ -3,8 +3,8 @@ const require_root = require("../root-rPH6FGDT.cjs");
3
3
  const require_calendarGrid = require("../calendarGrid-p8K-7cuB.cjs");
4
4
  const require_promptSafety = require("../promptSafety-CNEW3_qS.cjs");
5
5
  require("../collection/index.cjs");
6
- const require_discovery = require("../discovery-CDOjfctD.cjs");
7
- const require_server = require("../server-WU35UYlF.cjs");
6
+ const require_discovery = require("../discovery-CrA_EqUJ.cjs");
7
+ const require_server = require("../server-CorfPQVQ.cjs");
8
8
  const require_notifier = require("../notifier-DZJj0sOh.cjs");
9
9
  let node_fs_promises = require("node:fs/promises");
10
10
  //#region src/collection-watchers/config.ts
@@ -2,8 +2,8 @@ import { t as canonicalRoot } from "../root-BMroU_mB.js";
2
2
  import { k as fieldText } from "../calendarGrid-Csy2rpjp.js";
3
3
  import { E as whenMatches, f as itemIsDone } from "../promptSafety-ACXw_2Yu.js";
4
4
  import "../collection/index.js";
5
- import { K as collectionChangePayload, Q as peekWorkspaceRoot, d as storeFor, et as publishCollectionChange, n as discoverCollections, r as loadCollection, z as itemFilePath } from "../discovery-CC7kuwHf.js";
6
- import { d as isTriggerDue, f as maybeSpawnSuccessor } from "../server-AA2kfDfk.js";
5
+ import { K as collectionChangePayload, Q as peekWorkspaceRoot, d as storeFor, et as publishCollectionChange, n as discoverCollections, r as loadCollection, z as itemFilePath } from "../discovery-D7LCN8gJ.js";
6
+ import { d as isTriggerDue, f as maybeSpawnSuccessor } from "../server-D6QY853r.js";
7
7
  import { d as publish, m as updateForPlugin, n as clear, s as listAll } from "../notifier-DhnwR82U.js";
8
8
  import { access } from "node:fs/promises";
9
9
  //#region src/collection-watchers/config.ts
@@ -107,8 +107,12 @@ function getWorkspaceRoot() {
107
107
  function peekWorkspaceRoot() {
108
108
  return hostSlot.peek()?.workspaceRoot ?? null;
109
109
  }
110
- function userSkillsDir() {
111
- return requireHost().paths.userSkillsDir;
110
+ /** The user-scope skills dir for a root, or `null` when it has none. The one
111
+ * place the pre-3.3.0 `string` binding is normalized, so nothing downstream
112
+ * has to know the host might not have upgraded its shape yet. */
113
+ function userSkillsDir(workspaceRoot) {
114
+ const binding = requireHost().paths.userSkillsDir;
115
+ return typeof binding === "string" ? binding : binding(workspaceRoot);
112
116
  }
113
117
  function projectSkillsDir(workspaceRoot) {
114
118
  return requireHost().paths.projectSkillsDir(workspaceRoot);
@@ -2718,6 +2722,14 @@ async function collectFromDir(skillsRoot, source, workspaceRoot) {
2718
2722
  }
2719
2723
  return results;
2720
2724
  }
2725
+ /** The user-scope dir this call should scan, or `null` for none. The single
2726
+ * place the "explicit override beats the host binding, and either may say
2727
+ * none" rule is spelled — `??` cannot express it, because `undefined` there
2728
+ * means "ask the host" and would silently re-enable a scope the caller
2729
+ * passed `null` to switch off. */
2730
+ function resolveUserDir(opts, workspaceRoot) {
2731
+ return opts.userSkillsDir !== void 0 ? opts.userSkillsDir : userSkillsDir(workspaceRoot);
2732
+ }
2721
2733
  /** Discover every schema-driven collection available to this
2722
2734
  * workspace. Project-scope collections override user-scope on slug
2723
2735
  * collision. The `workspaceRoot` override also flows into each
@@ -2727,10 +2739,10 @@ async function collectFromDir(skillsRoot, source, workspaceRoot) {
2727
2739
  * regardless of override). */
2728
2740
  async function discoverCollections(opts = {}) {
2729
2741
  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();
2730
- const userDir = opts.userSkillsDir ?? userSkillsDir();
2742
+ const userDir = resolveUserDir(opts, workspaceRoot);
2731
2743
  const projectDir = projectSkillsDir(workspaceRoot);
2732
2744
  const feedCollections = await collectFromDir(feedsRoot(workspaceRoot), "feed", workspaceRoot);
2733
- const userCollections = await collectFromDir(userDir, "user", workspaceRoot);
2745
+ const userCollections = userDir === null ? [] : await collectFromDir(userDir, "user", workspaceRoot);
2734
2746
  const projectCollections = await collectFromDir(projectDir, "project", workspaceRoot);
2735
2747
  const merged = /* @__PURE__ */ new Map();
2736
2748
  for (const entry of feedCollections) merged.set(entry.slug, entry);
@@ -2744,10 +2756,10 @@ async function loadCollection(slug, opts = {}) {
2744
2756
  const safeName = safeSlugName(slug);
2745
2757
  if (safeName === null) return null;
2746
2758
  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();
2747
- const userDir = opts.userSkillsDir ?? userSkillsDir();
2759
+ const userDir = resolveUserDir(opts, workspaceRoot);
2748
2760
  const projectCollection = await loadOneCollection(projectSkillsDir(workspaceRoot), safeName, "project", workspaceRoot);
2749
2761
  if (projectCollection) return projectCollection;
2750
- const userCollection = await loadOneCollection(userDir, safeName, "user", workspaceRoot);
2762
+ const userCollection = userDir === null ? null : await loadOneCollection(userDir, safeName, "user", workspaceRoot);
2751
2763
  if (userCollection) return userCollection;
2752
2764
  return loadOneCollection(feedsRoot(workspaceRoot), safeName, "feed", workspaceRoot);
2753
2765
  }
@@ -3122,4 +3134,4 @@ Object.defineProperty(exports, "writeItem", {
3122
3134
  }
3123
3135
  });
3124
3136
 
3125
- //# sourceMappingURL=discovery-CDOjfctD.cjs.map
3137
+ //# sourceMappingURL=discovery-CrA_EqUJ.cjs.map