@sweny-ai/core 0.1.88 → 0.1.89

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.
@@ -14,14 +14,41 @@
14
14
  * and `../skills/index.js`; this CLI is a thin authoring shell over them.
15
15
  */
16
16
  import { Command } from "commander";
17
- declare const VALID_CATEGORIES: readonly ["general", "git", "tasks", "notification", "observability", "data"];
18
- type SkillCategory = (typeof VALID_CATEGORIES)[number];
17
+ import { type SkillCategory } from "../types.js";
18
+ declare const HARNESS_DIRS: {
19
+ readonly claude: ".claude/skills";
20
+ readonly sweny: ".sweny/skills";
21
+ readonly agents: ".agents/skills";
22
+ readonly gemini: ".gemini/skills";
23
+ };
24
+ type HarnessKey = keyof typeof HARNESS_DIRS;
19
25
  /** Build the SKILL.md body for a fresh scaffold. Exported for tests. */
20
26
  export declare function renderSkillTemplate(opts: {
21
27
  id: string;
22
28
  description: string;
23
29
  category: SkillCategory;
24
30
  }): string;
31
+ interface NewOptions {
32
+ description?: string;
33
+ category?: string;
34
+ harness?: HarnessKey;
35
+ force?: boolean;
36
+ }
37
+ /**
38
+ * Action handler for `sweny skill new`. Exported for unit tests; production
39
+ * code goes through `registerSkillCommand`. `cwd` defaults to `process.cwd()`
40
+ * so the CLI works as expected; tests pass a tmp dir for isolation.
41
+ */
42
+ export declare function runSkillNew(idArg: string, options: NewOptions, cwd?: string): void;
43
+ interface ListOptions {
44
+ json?: boolean;
45
+ }
46
+ /**
47
+ * Action handler for `sweny skill list`. Exported for unit tests; production
48
+ * code goes through `registerSkillCommand`. `cwd` and `env` injection lets
49
+ * tests exercise the configured-badge logic without polluting global state.
50
+ */
51
+ export declare function runSkillList(options: ListOptions, cwd?: string, env?: Record<string, string | undefined>): void;
25
52
  /** Register the `sweny skill` subcommand group on the parent program. */
26
53
  export declare function registerSkillCommand(program: Command): void;
27
54
  export {};
package/dist/cli/skill.js CHANGED
@@ -18,9 +18,9 @@ import * as path from "node:path";
18
18
  import chalk from "chalk";
19
19
  import { builtinSkills } from "../skills/index.js";
20
20
  import { configuredSkillsWithDiagnostics, discoverSkillsWithDiagnostics } from "../skills/custom-loader.js";
21
+ import { SKILL_CATEGORIES } from "../types.js";
21
22
  // Mirror of the loader's regex so authoring-side validation matches discovery.
22
23
  const VALID_SKILL_ID = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
23
- const VALID_CATEGORIES = ["general", "git", "tasks", "notification", "observability", "data"];
24
24
  const HARNESS_DIRS = {
25
25
  claude: ".claude/skills",
26
26
  sweny: ".sweny/skills",
@@ -80,7 +80,12 @@ export function renderSkillTemplate(opts) {
80
80
  lines.push("");
81
81
  return lines.join("\n");
82
82
  }
83
- function runSkillNew(idArg, options) {
83
+ /**
84
+ * Action handler for `sweny skill new`. Exported for unit tests; production
85
+ * code goes through `registerSkillCommand`. `cwd` defaults to `process.cwd()`
86
+ * so the CLI works as expected; tests pass a tmp dir for isolation.
87
+ */
88
+ export function runSkillNew(idArg, options, cwd = process.cwd()) {
84
89
  const id = idArg.toLowerCase();
85
90
  if (!VALID_SKILL_ID.test(id) || id.includes("--") || id.length > 64) {
86
91
  console.error(chalk.red(` Invalid skill id "${idArg}".\n Skill ids must be lowercase alphanumeric + hyphens (no consecutive hyphens), max 64 chars.`));
@@ -89,8 +94,8 @@ function runSkillNew(idArg, options) {
89
94
  }
90
95
  const description = (options.description ?? `Custom ${id} skill`).trim();
91
96
  const category = (options.category ?? "general");
92
- if (!VALID_CATEGORIES.includes(category)) {
93
- console.error(chalk.red(` Invalid category "${category}".\n Allowed: ${VALID_CATEGORIES.join(", ")}`));
97
+ if (!SKILL_CATEGORIES.includes(category)) {
98
+ console.error(chalk.red(` Invalid category "${category}".\n Allowed: ${SKILL_CATEGORIES.join(", ")}`));
94
99
  process.exit(2);
95
100
  return;
96
101
  }
@@ -101,7 +106,6 @@ function runSkillNew(idArg, options) {
101
106
  process.exit(2);
102
107
  return;
103
108
  }
104
- const cwd = process.cwd();
105
109
  const skillDir = path.join(cwd, baseDir, id);
106
110
  const skillFile = path.join(skillDir, "SKILL.md");
107
111
  if (fs.existsSync(skillFile) && !options.force) {
@@ -121,16 +125,20 @@ function runSkillNew(idArg, options) {
121
125
  console.log(chalk.dim(` 5. Run \`sweny skill list\` to confirm it discovers correctly.`));
122
126
  console.log();
123
127
  }
124
- function runSkillList(options) {
125
- const cwd = process.cwd();
126
- // We want the FULL list — built-in + custom — even when env vars
127
- // aren't set. configuredSkills filters by env, which hides authoring-
128
- // time skills the user just scaffolded. So combine sources directly.
128
+ /**
129
+ * Action handler for `sweny skill list`. Exported for unit tests; production
130
+ * code goes through `registerSkillCommand`. `cwd` and `env` injection lets
131
+ * tests exercise the configured-badge logic without polluting global state.
132
+ */
133
+ export function runSkillList(options, cwd = process.cwd(), env = process.env) {
134
+ // We want the FULL list (built-in + custom) even when env vars aren't set.
135
+ // configuredSkills filters by env, which hides authoring-time skills the
136
+ // user just scaffolded, so combine sources directly.
129
137
  const { skills: customSkills, warnings: customWarnings } = discoverSkillsWithDiagnostics(cwd);
130
138
  const customIds = new Set(customSkills.map((s) => s.id));
131
139
  const builtinList = builtinSkills.filter((s) => !customIds.has(s.id));
132
- // Configured map drives the "configured?" badge — required env vars present.
133
- const configuredIds = new Set(configuredSkillsWithDiagnostics(process.env, cwd).skills.map((s) => s.id));
140
+ // Configured map drives the "configured?" badge: required env vars present.
141
+ const configuredIds = new Set(configuredSkillsWithDiagnostics(env, cwd).skills.map((s) => s.id));
134
142
  if (options.json) {
135
143
  const data = [
136
144
  ...builtinList.map((s) => ({
@@ -188,7 +196,7 @@ export function registerSkillCommand(program) {
188
196
  .command("new <id>")
189
197
  .description("Scaffold a new SKILL.md in .claude/skills/<id>/")
190
198
  .option("-d, --description <text>", "One-line description (required field in frontmatter)")
191
- .option("-c, --category <category>", `Skill category (${VALID_CATEGORIES.join("|")})`, "general")
199
+ .option("-c, --category <category>", `Skill category (${SKILL_CATEGORIES.join("|")})`, "general")
192
200
  .option("--harness <harness>", `Where to place the skill (${Object.keys(HARNESS_DIRS).join("|")})`, "claude")
193
201
  .option("--force", "Overwrite an existing SKILL.md")
194
202
  .action((id, options) => runSkillNew(id, options));
package/dist/schema.d.ts CHANGED
@@ -241,7 +241,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
241
241
  id: string;
242
242
  name: string;
243
243
  description: string;
244
- category: "git" | "observability" | "tasks" | "notification" | "general";
244
+ category: "general" | "git" | "tasks" | "notification" | "observability";
245
245
  config: Record<string, {
246
246
  description: string;
247
247
  required?: boolean | undefined;
@@ -266,7 +266,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
266
266
  id: string;
267
267
  name: string;
268
268
  description: string;
269
- category: "git" | "observability" | "tasks" | "notification" | "general";
269
+ category: "general" | "git" | "tasks" | "notification" | "observability";
270
270
  instruction?: string | undefined;
271
271
  mcp?: {
272
272
  url?: string | undefined;
@@ -291,7 +291,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
291
291
  id: string;
292
292
  name: string;
293
293
  description: string;
294
- category: "git" | "observability" | "tasks" | "notification" | "general";
294
+ category: "general" | "git" | "tasks" | "notification" | "observability";
295
295
  config: Record<string, {
296
296
  description: string;
297
297
  required?: boolean | undefined;
@@ -316,7 +316,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
316
316
  id: string;
317
317
  name: string;
318
318
  description: string;
319
- category: "git" | "observability" | "tasks" | "notification" | "general";
319
+ category: "general" | "git" | "tasks" | "notification" | "observability";
320
320
  instruction?: string | undefined;
321
321
  mcp?: {
322
322
  url?: string | undefined;
@@ -11,6 +11,7 @@
11
11
  import { readdirSync, readFileSync, existsSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
  import YAML from "yaml";
14
+ import { SKILL_CATEGORIES } from "../types.js";
14
15
  import { builtinSkills, isSkillConfigured } from "./index.js";
15
16
  /** Directories to scan, in ascending priority order. Last match wins. */
16
17
  const SKILL_DIRS = [".gemini/skills", ".agents/skills", ".claude/skills", ".sweny/skills"];
@@ -99,8 +100,7 @@ function parseSkillMd(content, path) {
99
100
  }
100
101
  // Narrow the loosely-typed YAML output into the fields we expect
101
102
  const fm = raw;
102
- const VALID_CATEGORIES = ["general", "git", "tasks", "notification", "observability", "data"];
103
- const category = typeof fm.category === "string" && VALID_CATEGORIES.includes(fm.category)
103
+ const category = typeof fm.category === "string" && SKILL_CATEGORIES.includes(fm.category)
104
104
  ? fm.category
105
105
  : "general";
106
106
  const id = String(fm.name);
package/dist/types.d.ts CHANGED
@@ -22,8 +22,16 @@ export interface ConfigField {
22
22
  /** Default environment variable to read from */
23
23
  env?: string;
24
24
  }
25
- /** Skill categories — used for validation and grouping */
26
- export type SkillCategory = "git" | "observability" | "tasks" | "notification" | "data" | "general";
25
+ /**
26
+ * Skill categories: used for per-node validation and grouping.
27
+ *
28
+ * Single source of truth for runtime + compile-time. The `as const` tuple
29
+ * lets the CLI iterate it for help text and the loader use it for runtime
30
+ * validation; `SkillCategory` is derived so adding a category requires
31
+ * only this line.
32
+ */
33
+ export declare const SKILL_CATEGORIES: readonly ["general", "git", "tasks", "notification", "observability", "data"];
34
+ export type SkillCategory = (typeof SKILL_CATEGORIES)[number];
27
35
  /** A skill groups related tools with shared config requirements */
28
36
  export interface Skill {
29
37
  id: string;
package/dist/types.js CHANGED
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Skill categories: used for per-node validation and grouping.
3
+ *
4
+ * Single source of truth for runtime + compile-time. The `as const` tuple
5
+ * lets the CLI iterate it for help text and the loader use it for runtime
6
+ * validation; `SkillCategory` is derived so adding a category requires
7
+ * only this line.
8
+ */
9
+ export const SKILL_CATEGORIES = ["general", "git", "tasks", "notification", "observability", "data"];
1
10
  export const consoleLogger = {
2
11
  info: (msg, data) => console.log(`[info] ${msg}`, data ?? ""),
3
12
  warn: (msg, data) => console.warn(`[warn] ${msg}`, data ?? ""),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.88",
3
+ "version": "0.1.89",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"