@senpi/runtime 1.1.0-dev.runtime-phase-2.20260513122539 → 1.1.0-dev.runtime-phase-2.20260514134437

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.
Files changed (42) hide show
  1. package/dist/cli/senpi-cli-config.d.ts +15 -1
  2. package/dist/cli/senpi-cli-config.d.ts.map +1 -1
  3. package/dist/cli/senpi-cli-config.js +19 -2
  4. package/dist/cli/senpi-cli-config.js.map +1 -1
  5. package/dist/cli/senpi-commands.d.ts.map +1 -1
  6. package/dist/cli/senpi-commands.js +4 -1
  7. package/dist/cli/senpi-commands.js.map +1 -1
  8. package/dist/index.d.ts +17 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +32 -10
  11. package/dist/index.js.map +1 -1
  12. package/dist/runtime/auto-update/coordinator.d.ts +13 -0
  13. package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
  14. package/dist/runtime/auto-update/coordinator.js +14 -0
  15. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  16. package/dist/skills-manager/bootstrap.d.ts.map +1 -1
  17. package/dist/skills-manager/bootstrap.js +34 -16
  18. package/dist/skills-manager/bootstrap.js.map +1 -1
  19. package/dist/skills-manager/constants.d.ts +20 -1
  20. package/dist/skills-manager/constants.d.ts.map +1 -1
  21. package/dist/skills-manager/constants.js +26 -1
  22. package/dist/skills-manager/constants.js.map +1 -1
  23. package/dist/skills-manager/coordinator.d.ts +147 -20
  24. package/dist/skills-manager/coordinator.d.ts.map +1 -1
  25. package/dist/skills-manager/coordinator.js +260 -86
  26. package/dist/skills-manager/coordinator.js.map +1 -1
  27. package/dist/skills-manager/state-store.d.ts +27 -4
  28. package/dist/skills-manager/state-store.d.ts.map +1 -1
  29. package/dist/skills-manager/state-store.js +42 -16
  30. package/dist/skills-manager/state-store.js.map +1 -1
  31. package/dist/skills-manager/types.d.ts +46 -8
  32. package/dist/skills-manager/types.d.ts.map +1 -1
  33. package/dist/skills-manager/utils.d.ts +17 -0
  34. package/dist/skills-manager/utils.d.ts.map +1 -1
  35. package/dist/skills-manager/utils.js +43 -11
  36. package/dist/skills-manager/utils.js.map +1 -1
  37. package/dist/skills-manager/version-manager.d.ts +93 -8
  38. package/dist/skills-manager/version-manager.d.ts.map +1 -1
  39. package/dist/skills-manager/version-manager.js +185 -28
  40. package/dist/skills-manager/version-manager.js.map +1 -1
  41. package/openclaw.plugin.json +7 -9
  42. package/package.json +1 -1
@@ -9,10 +9,23 @@ import type { LoggerLike, SkillsManagerState } from "./types.js";
9
9
  */
10
10
  export declare class SkillsManagerStateStore {
11
11
  private readonly stateDir;
12
+ /**
13
+ * Skills directory search path. Single-string form is accepted for
14
+ * backward compat with legacy callers; arrays follow the standard
15
+ * "primary first, mirrors after" convention.
16
+ */
12
17
  private readonly skillsDir;
13
18
  private readonly logger?;
14
19
  private readonly filePath;
15
- constructor(stateDir: string, skillsDir: string, logger?: LoggerLike | undefined);
20
+ constructor(stateDir: string,
21
+ /**
22
+ * Skills directory search path. Single-string form is accepted for
23
+ * backward compat with legacy callers; arrays follow the standard
24
+ * "primary first, mirrors after" convention.
25
+ */
26
+ skillsDir: string | readonly string[], logger?: LoggerLike | undefined);
27
+ /** All configured dirs as an array, regardless of how the constructor was called. */
28
+ private get skillsDirs();
16
29
  /**
17
30
  * Load persisted state, falling back to defaults when the file is absent
18
31
  * (first run) or unreadable / corrupt.
@@ -25,9 +38,19 @@ export declare class SkillsManagerStateStore {
25
38
  /** Atomically write state; creates the directory if it does not exist. */
26
39
  save(state: SkillsManagerState): Promise<void>;
27
40
  /**
28
- * Called on first run (no state file yet). Reads `skillsDir` and creates
29
- * a default {@link SkillState} entry for every installed skill so the first
30
- * poll tick starts with a populated state rather than an empty map.
41
+ * Called on first run (no state file yet). Walks every dir in the
42
+ * `skillsDir` search path and creates a default {@link SkillState} entry
43
+ * for each installed skill so the first poll tick starts with a populated
44
+ * state rather than an empty map.
45
+ *
46
+ * Multi-dir semantics: dirs are scanned in order; the first dir containing
47
+ * a given skill name wins. This matches the read priority used elsewhere
48
+ * (`readLocalVersion`, bootstrap pre-check) — the primary dir's view of a
49
+ * skill is canonical, secondary mirrors are fallbacks only.
50
+ *
51
+ * Per-dir ENOENT is silent (dir does not exist yet); any other I/O error
52
+ * is logged as a warning and the scan continues into the next dir, so one
53
+ * misconfigured mirror cannot wipe the whole initial state.
31
54
  */
32
55
  private buildInitialState;
33
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/skills-manager/state-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIjE;;;GAGG;AACH,qBAAa,uBAAuB;IAIhC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAL1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAGf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,YAAA;IAKtC;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAkDzC,0EAA0E;IACpE,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD;;;;OAIG;YACW,iBAAiB;CAoBhC"}
1
+ {"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/skills-manager/state-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIjE;;;GAGG;AACH,qBAAa,uBAAuB;IAIhC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAV1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAGf,QAAQ,EAAE,MAAM;IACjC;;;;OAIG;IACc,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,MAAM,CAAC,EAAE,UAAU,YAAA;IAKtC,qFAAqF;IACrF,OAAO,KAAK,UAAU,GAErB;IAED;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAkDzC,0EAA0E;IACpE,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD;;;;;;;;;;;;;;OAcG;YACW,iBAAiB;CAwBhC"}
@@ -16,12 +16,22 @@ export class SkillsManagerStateStore {
16
16
  skillsDir;
17
17
  logger;
18
18
  filePath;
19
- constructor(stateDir, skillsDir, logger) {
19
+ constructor(stateDir,
20
+ /**
21
+ * Skills directory search path. Single-string form is accepted for
22
+ * backward compat with legacy callers; arrays follow the standard
23
+ * "primary first, mirrors after" convention.
24
+ */
25
+ skillsDir, logger) {
20
26
  this.stateDir = stateDir;
21
27
  this.skillsDir = skillsDir;
22
28
  this.logger = logger;
23
29
  this.filePath = join(stateDir, SKILLS_MANAGER_STATE_FILE);
24
30
  }
31
+ /** All configured dirs as an array, regardless of how the constructor was called. */
32
+ get skillsDirs() {
33
+ return typeof this.skillsDir === "string" ? [this.skillsDir] : this.skillsDir;
34
+ }
25
35
  /**
26
36
  * Load persisted state, falling back to defaults when the file is absent
27
37
  * (first run) or unreadable / corrupt.
@@ -76,28 +86,44 @@ export class SkillsManagerStateStore {
76
86
  await writeFile(this.filePath, JSON.stringify(state, null, 2), "utf8");
77
87
  }
78
88
  /**
79
- * Called on first run (no state file yet). Reads `skillsDir` and creates
80
- * a default {@link SkillState} entry for every installed skill so the first
81
- * poll tick starts with a populated state rather than an empty map.
89
+ * Called on first run (no state file yet). Walks every dir in the
90
+ * `skillsDir` search path and creates a default {@link SkillState} entry
91
+ * for each installed skill so the first poll tick starts with a populated
92
+ * state rather than an empty map.
93
+ *
94
+ * Multi-dir semantics: dirs are scanned in order; the first dir containing
95
+ * a given skill name wins. This matches the read priority used elsewhere
96
+ * (`readLocalVersion`, bootstrap pre-check) — the primary dir's view of a
97
+ * skill is canonical, secondary mirrors are fallbacks only.
98
+ *
99
+ * Per-dir ENOENT is silent (dir does not exist yet); any other I/O error
100
+ * is logged as a warning and the scan continues into the next dir, so one
101
+ * misconfigured mirror cannot wipe the whole initial state.
82
102
  */
83
103
  async buildInitialState() {
84
- try {
85
- const entries = await readdir(this.skillsDir, { withFileTypes: true });
86
- const skills = {};
87
- for (const entry of entries) {
88
- if (entry.isDirectory() && !entry.name.startsWith(".")) {
89
- const installedVersion = await readLocalVersion(this.skillsDir, entry.name, this.logger);
104
+ const skills = {};
105
+ for (const dir of this.skillsDirs) {
106
+ try {
107
+ const entries = await readdir(dir, { withFileTypes: true });
108
+ for (const entry of entries) {
109
+ if (!entry.isDirectory() || entry.name.startsWith("."))
110
+ continue;
111
+ // First-found wins — secondary dirs cannot override the primary's
112
+ // entry once it's been written into the map.
113
+ if (skills[entry.name])
114
+ continue;
115
+ const installedVersion = await readLocalVersion(dir, entry.name, this.logger);
90
116
  skills[entry.name] = { ...createDefaultSkillState(entry.name), installedVersion };
91
117
  }
92
118
  }
93
- return { skills };
94
- }
95
- catch (err) {
96
- if (err.code !== "ENOENT") {
97
- this.logger?.warn(`[senpi-skills-manager] failed to read skills directory, starting with empty skill state | path=${this.skillsDir} err=${formatErr(err)}`);
119
+ catch (err) {
120
+ if (err.code !== "ENOENT") {
121
+ this.logger?.warn(`[senpi-skills-manager] failed to read skills directory, continuing with remaining dirs | path=${dir} err=${formatErr(err)}`);
122
+ }
123
+ // Continue scanning the remaining dirs even on error — don't bail.
98
124
  }
99
- return createDefaultSkillsManagerState();
100
125
  }
126
+ return Object.keys(skills).length > 0 ? { skills } : createDefaultSkillsManagerState();
101
127
  }
102
128
  }
103
129
  //# sourceMappingURL=state-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-store.js","sourceRoot":"","sources":["../../src/skills-manager/state-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-store.js","sourceRoot":"","sources":["../../src/skills-manager/state-store.ts"],"names":[],"mappings":"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"}
@@ -12,15 +12,29 @@ export type SkillsManagerHealth = "healthy" | "degraded" | "unhealthy" | "disabl
12
12
  */
13
13
  export interface RawSkillsManagerConfig {
14
14
  enabled?: boolean;
15
- skillsDir?: string;
16
- pollIntervalMinutes?: number;
15
+ /**
16
+ * Skills install/search directory.
17
+ *
18
+ * Accepts either a single string (legacy form) or an array of strings
19
+ * (search path, first is the primary write target). `resolveSkillsManagerConfig`
20
+ * normalises both shapes to a non-empty `string[]` and falls back to
21
+ * `DEFAULT_SKILLS_DIRS` when omitted, null, an empty array, or wrong-typed.
22
+ */
23
+ skillsDir?: string | string[];
17
24
  skills?: string[];
18
25
  }
19
26
  /** Resolved config — all fields have safe defaults filled in. */
20
27
  export interface SkillsManagerConfig {
21
28
  enabled: boolean;
22
- skillsDir: string;
23
- pollIntervalMinutes: number;
29
+ /**
30
+ * Skills directory search path. Always a non-empty array after
31
+ * resolution; `[0]` is the **primary** (writes go here), the rest are
32
+ * read-only mirrors scanned during version checks and discovery.
33
+ *
34
+ * For backward compat with code that wants a single string (logs, snapshot
35
+ * fields, single-dir helpers), use `skillsDir[0]`.
36
+ */
37
+ skillsDir: string[];
24
38
  /**
25
39
  * Skill names this coordinator manages.
26
40
  *
@@ -52,12 +66,16 @@ export interface SkillsManagerState {
52
66
  /** Read-only snapshot for gateway RPCs and health views. */
53
67
  export interface SkillsManagerSnapshot {
54
68
  enabled: boolean;
55
- skillsDir: string;
69
+ /**
70
+ * Skills directory search path. Mirrors `SkillsManagerConfig.skillsDir` — a
71
+ * non-empty array where `[0]` is the primary write target. Callers
72
+ * rendering this for humans should join with commas (e.g. `dirs.join(", ")`).
73
+ */
74
+ skillsDir: string[];
56
75
  health: SkillsManagerHealth;
57
76
  lastAction: string;
58
77
  lastError: string | null;
59
78
  skills: SkillSnapshot[];
60
- nextCheckAt: string | null;
61
79
  }
62
80
  /** Per-skill entry in a snapshot. */
63
81
  export interface SkillSnapshot {
@@ -73,14 +91,34 @@ export interface SkillVersionInfo {
73
91
  localVersion: string | null;
74
92
  remoteVersion: string | null;
75
93
  updateKind: SkillUpdateKind;
94
+ /**
95
+ * Short description parsed from the **remote** SKILL.md frontmatter (the
96
+ * version the user is being notified about), surfaced back to the
97
+ * coordinator so notification text can include an "About:" line that
98
+ * reflects the upcoming version rather than the potentially-stale local
99
+ * copy.
100
+ *
101
+ * Null when:
102
+ * - The remote fetch returned 404 (skill not in the remote repo).
103
+ * - The remote SKILL.md is present but its frontmatter has no
104
+ * `description` field.
105
+ *
106
+ * Optional in the interface for backward compatibility with injected
107
+ * test deps that only return version data; the real `checkSkillVersion`
108
+ * always populates it (possibly null) on every return path.
109
+ */
110
+ remoteDescription?: string | null;
76
111
  }
77
112
  /** Injectable overrides used in unit tests to avoid network and git calls. */
78
113
  export interface SkillsUpdateCoordinatorDeps {
79
114
  /**
80
- * Override version check — called as (skillsDir, skillName, rawBaseUrl, logger).
115
+ * Override version check — called as (skillsDirs, skillName, rawBaseUrl, logger).
81
116
  * Defaults to the real checkSkillVersion with fetchImpl omitted (uses global fetch).
117
+ *
118
+ * `skillsDirs` is the search-path array from the resolved config; the real
119
+ * `checkSkillVersion` tries each in order when reading SKILL.md.
82
120
  */
83
- checkSkillVersion?: (skillsDir: string, skillName: string, rawBaseUrl: string, logger?: LoggerLike) => Promise<SkillVersionInfo>;
121
+ checkSkillVersion?: (skillsDirs: readonly string[], skillName: string, rawBaseUrl: string, logger?: LoggerLike) => Promise<SkillVersionInfo>;
84
122
  /**
85
123
  * Override clone + install — receives the list of skill names that need
86
124
  * updating and the config. Defaults to real cloneSkillsRepo + installSkill.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/skills-manager/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/skills-manager/types.ts"],"names":[],"mappings":"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"}
@@ -43,4 +43,21 @@ export declare function createDefaultSkillsManagerState(): SkillsManagerState;
43
43
  export declare function createDefaultSkillState(name: string): SkillState;
44
44
  /** Formats an unknown thrown value as a short string for log messages. */
45
45
  export declare function formatErr(e: unknown): string;
46
+ /**
47
+ * Normalise the raw `skillsDir` value into a non-empty `string[]` (the
48
+ * "search path" form used internally).
49
+ *
50
+ * Resolution rules:
51
+ * - Single string → wrap as `[value]` (legacy single-dir form)
52
+ * - Array → filter out non-strings and empty/whitespace entries; if anything
53
+ * survives, use the filtered array
54
+ * - Anything else (undefined, null, empty array post-filter, wrong type) →
55
+ * fall back to `DEFAULT_SKILLS_DIRS` (which itself reads `SENPI_SKILLS_DIR`
56
+ * as a comma-separated list, or the canonical install path as the final
57
+ * fallback)
58
+ *
59
+ * Always returns a non-empty array — callers can rely on `[0]` existing as
60
+ * the *primary* (writes go here; reads scan in order).
61
+ */
62
+ export declare function normalizeSkillsDir(value: unknown): string[];
46
63
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/skills-manager/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAA8B,CAAC;AAEnF;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,CAAC,EAAE,sBAAsB,GAAG,IAAI,GACpC,mBAAmB,CAiBrB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,IAAI,kBAAkB,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAWhE;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAE5C"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/skills-manager/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAA8B,CAAC;AAEnF;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,CAAC,EAAE,sBAAsB,GAAG,IAAI,GACpC,mBAAmB,CAcrB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,IAAI,kBAAkB,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAWhE;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAE5C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAc3D"}
@@ -1,4 +1,4 @@
1
- import { DEFAULT_SKILLS_DIR } from "./constants.js";
1
+ import { DEFAULT_SKILLS_DIRS } from "./constants.js";
2
2
  /**
3
3
  * Canonical default list of managed skill names when the user omits the
4
4
  * `skills` field. Kept in sync with the JSON Schema default in
@@ -28,10 +28,7 @@ export function resolveSkillsManagerConfig(value) {
28
28
  const raw = value ?? {};
29
29
  return {
30
30
  enabled: toBool(raw.enabled, true),
31
- skillsDir: typeof raw.skillsDir === "string" && raw.skillsDir.length > 0
32
- ? raw.skillsDir
33
- : DEFAULT_SKILLS_DIR,
34
- pollIntervalMinutes: clamp(toFinite(raw.pollIntervalMinutes, 60), 1, 24 * 60),
31
+ skillsDir: normalizeSkillsDir(raw.skillsDir),
35
32
  // Field-omitted vs explicit-empty distinction matters here:
36
33
  // - undefined / null / non-array → use canonical default (matches the
37
34
  // JSON Schema default in openclaw.plugin.json — keeps wizard-written
@@ -75,13 +72,48 @@ export function createDefaultSkillState(name) {
75
72
  export function formatErr(e) {
76
73
  return e instanceof Error ? e.message : String(e);
77
74
  }
75
+ /**
76
+ * Normalise the raw `skillsDir` value into a non-empty `string[]` (the
77
+ * "search path" form used internally).
78
+ *
79
+ * Resolution rules:
80
+ * - Single string → wrap as `[value]` (legacy single-dir form)
81
+ * - Array → filter out non-strings and empty/whitespace entries; if anything
82
+ * survives, use the filtered array
83
+ * - Anything else (undefined, null, empty array post-filter, wrong type) →
84
+ * fall back to `DEFAULT_SKILLS_DIRS` (which itself reads `SENPI_SKILLS_DIR`
85
+ * as a comma-separated list, or the canonical install path as the final
86
+ * fallback)
87
+ *
88
+ * Always returns a non-empty array — callers can rely on `[0]` existing as
89
+ * the *primary* (writes go here; reads scan in order).
90
+ */
91
+ export function normalizeSkillsDir(value) {
92
+ if (typeof value === "string") {
93
+ const trimmed = value.trim();
94
+ if (trimmed.length > 0)
95
+ return [trimmed];
96
+ }
97
+ else if (Array.isArray(value)) {
98
+ const filtered = value
99
+ .filter((v) => typeof v === "string")
100
+ .map((s) => s.trim())
101
+ .filter((s) => s.length > 0);
102
+ if (filtered.length > 0)
103
+ return filtered;
104
+ }
105
+ // DEFAULT_SKILLS_DIRS is a readonly tuple at module level — return a fresh
106
+ // mutable copy so callers that mutate (e.g. tests) don't poison the global.
107
+ return [...DEFAULT_SKILLS_DIRS];
108
+ }
109
+ // ─── Local config-coercion helpers ──────────────────────────────────────────
110
+ // `toBool` is intentionally duplicated from `src/runtime/auto-update/types.ts`
111
+ // to keep `src/skills-manager/` free of any compile- or runtime-time
112
+ // dependency on the auto-update package. An earlier attempt to consolidate
113
+ // this style of helper across the two modules (commit 48592db, "consolidate
114
+ // formatErr into policy.ts") was reverted in 5a94af8 with the explicit rule
115
+ // "do not touch auto-update module" — same rationale applies here.
78
116
  function toBool(value, fallback) {
79
117
  return typeof value === "boolean" ? value : fallback;
80
118
  }
81
- function toFinite(value, fallback) {
82
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
83
- }
84
- function clamp(v, min, max) {
85
- return Math.min(max, Math.max(min, v));
86
- }
87
119
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/skills-manager/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAQpD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,uBAAuB,CAAC,CAAC;AAEnF;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAqC;IAErC,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE,CAAC;IACxB,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;QAClC,SAAS,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YACtE,CAAC,CAAC,GAAG,CAAC,SAAS;YACf,CAAC,CAAC,kBAAkB;QACtB,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;QAC7E,4DAA4D;QAC5D,sEAAsE;QACtE,uEAAuE;QACvE,6BAA6B;QAC7B,oEAAoE;QACpE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAC/B,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;YACjE,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B;IAC7C,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO;QACL,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,IAAI;QACnB,kBAAkB,EAAE,IAAI;QACxB,wBAAwB,EAAE,IAAI;QAC9B,mBAAmB,EAAE,CAAC;QACtB,aAAa,EAAE,IAAI;QACnB,iBAAiB,EAAE,IAAI;KACxB,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,CAAU;IAClC,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,MAAM,CAAC,KAAc,EAAE,QAAiB;IAC/C,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AACvD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc,EAAE,QAAgB;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChF,CAAC;AAED,SAAS,KAAK,CAAC,CAAS,EAAE,GAAW,EAAE,GAAW;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/skills-manager/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQrD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,uBAAuB,CAAC,CAAC;AAEnF;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAqC;IAErC,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE,CAAC;IACxB,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;QAClC,SAAS,EAAE,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC;QAC5C,4DAA4D;QAC5D,sEAAsE;QACtE,uEAAuE;QACvE,6BAA6B;QAC7B,oEAAoE;QACpE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAC/B,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;YACjE,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B;IAC7C,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO;QACL,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,IAAI;QACnB,kBAAkB,EAAE,IAAI;QACxB,wBAAwB,EAAE,IAAI;QAC9B,mBAAmB,EAAE,CAAC;QACtB,aAAa,EAAE,IAAI;QACnB,iBAAiB,EAAE,IAAI;KACxB,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,CAAU;IAClC,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,KAAK;aACnB,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;aACjD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC;IAC3C,CAAC;IACD,2EAA2E;IAC3E,4EAA4E;IAC5E,OAAO,CAAC,GAAG,mBAAmB,CAAC,CAAC;AAClC,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,mEAAmE;AACnE,SAAS,MAAM,CAAC,KAAc,EAAE,QAAiB;IAC/C,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AACvD,CAAC"}
@@ -26,18 +26,91 @@ export declare function normalizeVersion(v: string): string;
26
26
  * Returns null when the frontmatter block is absent or the field is missing.
27
27
  */
28
28
  export declare function parseSkillMdVersion(content: string): string | null;
29
+ /**
30
+ * Extract the `description` value from a SKILL.md YAML frontmatter block.
31
+ *
32
+ * Skill descriptions can be authored a few different ways. This helper
33
+ * handles the real-world shapes that appear in the senpi-skills repo today:
34
+ *
35
+ * 1. Single-line, double-quoted:
36
+ * description: "Configure, deploy, ..."
37
+ * 2. Single-line, single-quoted or unquoted:
38
+ * description: Manage runtimes ...
39
+ * 3. YAML block scalar (folded or literal, optional chomping indicator):
40
+ * description: >
41
+ * A short summary
42
+ * continues on this line.
43
+ * description: >- # strip trailing newlines (this PR's senpi-skills repo)
44
+ * A short summary
45
+ * description: | # literal — preserves newlines (collapsed to spaces here)
46
+ * A short summary
47
+ * description: |+ # keep trailing newlines (same handling)
48
+ *
49
+ * The block scalar header is `[>|][-+]?` per the YAML 1.2 spec:
50
+ * `>` = folded, `|` = literal; `-` strips trailing newlines,
51
+ * `+` keeps them, no indicator clips. For notification rendering we
52
+ * always collapse to a single line joined by spaces, so the chomping
53
+ * indicator only affects whether we match the block-scalar branch.
54
+ *
55
+ * Returns null when the field is absent or the frontmatter block is missing.
56
+ *
57
+ * Note: kept as a regex extractor rather than introducing a full YAML
58
+ * parse here — `parseSkillMdVersion` uses the same approach, and skill
59
+ * frontmatters are constrained enough that the regex covers the shapes we
60
+ * actually encounter. If we ever adopt a full YAML parser for this file,
61
+ * both helpers should switch together.
62
+ */
63
+ export declare function parseSkillMdDescription(content: string): string | null;
64
+ /**
65
+ * Read both the installed version and description for a skill from its local
66
+ * SKILL.md. Shared core for `readLocalVersion` and `readLocalDescription` so a
67
+ * tick that needs both pays a single file read per dir rather than two.
68
+ *
69
+ * Multi-dir search path: tries each entry in order and returns the first
70
+ * readable SKILL.md's parsed fields. Either field may be null if absent in
71
+ * the frontmatter — that's distinct from "no SKILL.md found at all" (which
72
+ * returns nulls only after every dir has been tried).
73
+ */
74
+ export declare function readLocalSkillMetadata(skillsDir: string | readonly string[], skillName: string, logger?: LoggerLike): Promise<{
75
+ version: string | null;
76
+ description: string | null;
77
+ }>;
29
78
  /**
30
79
  * Read the installed version for a skill from its local SKILL.md.
31
- * Returns null when the file is absent or the version field is missing.
32
- * Logs a warning for unexpected errors (anything other than ENOENT).
80
+ *
81
+ * Accepts either a single dir (legacy callers) or an array of dirs (new
82
+ * multi-dir search path). When given an array, tries each entry in order and
83
+ * returns the first non-null match — this mirrors the documented behaviour
84
+ * that `skillsDir[0]` is the *primary* (writes go here) and the remaining
85
+ * entries are read-only fallbacks scanned during version checks.
86
+ *
87
+ * Returns null when no readable SKILL.md is found across any of the
88
+ * candidate dirs, or when the version field is absent. Logs a warning per
89
+ * dir for unexpected errors (anything other than ENOENT).
33
90
  */
34
- export declare function readLocalVersion(skillsDir: string, skillName: string, logger?: LoggerLike): Promise<string | null>;
91
+ export declare function readLocalVersion(skillsDir: string | readonly string[], skillName: string, logger?: LoggerLike): Promise<string | null>;
35
92
  /**
36
- * Fetch the remote SKILL.md for a skill and return its version.
93
+ * Read the installed description for a skill from its local SKILL.md.
94
+ * Multi-dir search path semantics identical to `readLocalVersion`.
95
+ *
96
+ * Returns null when no readable SKILL.md is found across any of the
97
+ * candidate dirs, or when the description field is absent.
98
+ */
99
+ export declare function readLocalDescription(skillsDir: string | readonly string[], skillName: string, logger?: LoggerLike): Promise<string | null>;
100
+ /**
101
+ * Fetch the remote SKILL.md for a skill and return both the version and the
102
+ * description in one network round-trip.
37
103
  *
38
- * Returns null when the skill does not exist in the remote repo (HTTP 404) —
39
- * that's a legitimate state ("skill removed or not yet published"), not an
40
- * error, so it should not pollute failure counters.
104
+ * Notification text uses the description from the **remote** SKILL.md (not
105
+ * the local one) so the "About:" line reflects the version the user is being
106
+ * asked about — a description on disk for v1.x may be stale relative to the
107
+ * v2.0.0 the major-update notification is announcing.
108
+ *
109
+ * Returns null fields when the skill does not exist in the remote repo
110
+ * (HTTP 404) — that's a legitimate state ("skill removed or not yet
111
+ * published"), not an error, so it should not pollute failure counters.
112
+ * Both fields may also be null when the remote SKILL.md is present but
113
+ * its frontmatter omits the corresponding field.
41
114
  *
42
115
  * Throws on transient/operational failures (network errors, timeouts, 5xx
43
116
  * responses, body-parse failures) so the caller's failure-recording path can
@@ -45,6 +118,18 @@ export declare function readLocalVersion(skillsDir: string, skillName: string, l
45
118
  *
46
119
  * @param fetchImpl Injectable for unit testing — defaults to global `fetch`.
47
120
  */
121
+ export declare function fetchRemoteSkillMetadata(skillName: string, rawBaseUrl?: string, fetchImpl?: typeof fetch): Promise<{
122
+ version: string | null;
123
+ description: string | null;
124
+ }>;
125
+ /**
126
+ * Fetch the remote SKILL.md for a skill and return its version.
127
+ *
128
+ * Thin wrapper over {@link fetchRemoteSkillMetadata} kept for backward
129
+ * compatibility with callers (and tests) that only need the version field.
130
+ * New code that wants both version + description in a single round-trip
131
+ * should call `fetchRemoteSkillMetadata` directly.
132
+ */
48
133
  export declare function fetchRemoteVersion(skillName: string, rawBaseUrl?: string, fetchImpl?: typeof fetch): Promise<string | null>;
49
134
  /**
50
135
  * Compare the local and remote versions for a skill and return a classified diff.
@@ -57,5 +142,5 @@ export declare function fetchRemoteVersion(skillName: string, rawBaseUrl?: strin
57
142
  * - Remote version is absent or unparseable → "unknown", caller should log and
58
143
  * skip.
59
144
  */
60
- export declare function checkSkillVersion(skillsDir: string, skillName: string, rawBaseUrl?: string, fetchImpl?: typeof fetch, logger?: LoggerLike): Promise<SkillVersionInfo>;
145
+ export declare function checkSkillVersion(skillsDir: string | readonly string[], skillName: string, rawBaseUrl?: string, fetchImpl?: typeof fetch, logger?: LoggerLike): Promise<SkillVersionInfo>;
61
146
  //# sourceMappingURL=version-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-manager.d.ts","sourceRoot":"","sources":["../../src/skills-manager/version-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAmB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGhF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMlE;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,EACxC,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,EACxC,SAAS,GAAE,OAAO,KAAa,EAC/B,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAyC3B"}
1
+ {"version":3,"file":"version-manager.d.ts","sourceRoot":"","sources":["../../src/skills-manager/version-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAmB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGhF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA6CtE;AASD;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA2BjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,EACxC,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAkCjE;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,EACxC,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,EACxC,SAAS,GAAE,OAAO,KAAa,EAC/B,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAgD3B"}