@sweny-ai/core 0.1.87 → 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.
- package/dist/cli/main.js +15 -7
- package/dist/cli/skill.d.ts +54 -0
- package/dist/cli/skill.js +208 -0
- package/dist/schema.d.ts +4 -4
- package/dist/skills/custom-loader.js +5 -1
- package/dist/types.d.ts +10 -2
- package/dist/types.js +9 -0
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -30,6 +30,7 @@ import { c, formatBanner, getStepDetails, formatStepLine, formatDagResultHuman,
|
|
|
30
30
|
import { checkProviderConnectivity } from "./check.js";
|
|
31
31
|
import { registerSetupCommand } from "./setup.js";
|
|
32
32
|
import { registerPublishCommand } from "./publish.js";
|
|
33
|
+
import { registerSkillCommand } from "./skill.js";
|
|
33
34
|
import { reportToCloud } from "./cloud-report.js";
|
|
34
35
|
import { runUpgrade, fetchLatestFromNpm } from "./upgrade.js";
|
|
35
36
|
import { maybeNudge, defaultCachePath } from "./version-check.js";
|
|
@@ -101,6 +102,7 @@ program
|
|
|
101
102
|
});
|
|
102
103
|
registerSetupCommand(program);
|
|
103
104
|
registerPublishCommand(program);
|
|
105
|
+
registerSkillCommand(program);
|
|
104
106
|
// ── Credential map builder ──────────────────────────────────────────
|
|
105
107
|
// buildCredentialMap lives in ./credentials.ts so tests can import it
|
|
106
108
|
// without triggering main.ts's top-level program.parse() side effect.
|
|
@@ -601,15 +603,21 @@ export async function workflowRunAction(file, options) {
|
|
|
601
603
|
console.error(chalk.red(`\n Workflow cannot run — missing required skills:\n`));
|
|
602
604
|
for (const err of validation.errors)
|
|
603
605
|
console.error(chalk.red(` \u2717 ${err}`));
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
606
|
+
const unknown = validation.missing.filter((m) => m.category === "unknown");
|
|
607
|
+
const envGaps = validation.missing.filter((m) => m.category !== "unknown" && m.missingEnv.length > 0);
|
|
608
|
+
if (unknown.length > 0) {
|
|
609
|
+
console.error(chalk.dim(`\n These skill IDs aren't built-in or discovered in .{claude,sweny,agents,gemini}/skills/.\n Scaffold one with:\n`));
|
|
610
|
+
for (const m of unknown) {
|
|
611
|
+
console.error(chalk.dim(` sweny skill new ${m.id}`));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (envGaps.length > 0) {
|
|
615
|
+
console.error(chalk.dim(`\n These skills are built-in but their env vars aren't set:`));
|
|
616
|
+
for (const m of envGaps) {
|
|
617
|
+
console.error(chalk.dim(` - ${m.id}: ${m.missingEnv.join(", ")}`));
|
|
610
618
|
}
|
|
611
619
|
}
|
|
612
|
-
console.error(
|
|
620
|
+
console.error(chalk.dim(`\n Run \`sweny skill list\` to see what's available.\n`));
|
|
613
621
|
process.exit(1);
|
|
614
622
|
return;
|
|
615
623
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sweny skill` command group — discover, list, and scaffold skills.
|
|
3
|
+
*
|
|
4
|
+
* Skills are the unit of capability a workflow node names in its `skills`
|
|
5
|
+
* array. Sweny ships built-ins (github, linear, slack, ...) and discovers
|
|
6
|
+
* custom skills from `.{gemini,agents,claude,sweny}/skills/<id>/SKILL.md`.
|
|
7
|
+
*
|
|
8
|
+
* This module owns the *authoring* side of that loop:
|
|
9
|
+
*
|
|
10
|
+
* sweny skill new <id> scaffold a SKILL.md template
|
|
11
|
+
* sweny skill list list built-in + custom skills together
|
|
12
|
+
*
|
|
13
|
+
* Discovery and validation already live in `../skills/custom-loader.js`
|
|
14
|
+
* and `../skills/index.js`; this CLI is a thin authoring shell over them.
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from "commander";
|
|
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;
|
|
25
|
+
/** Build the SKILL.md body for a fresh scaffold. Exported for tests. */
|
|
26
|
+
export declare function renderSkillTemplate(opts: {
|
|
27
|
+
id: string;
|
|
28
|
+
description: string;
|
|
29
|
+
category: SkillCategory;
|
|
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;
|
|
52
|
+
/** Register the `sweny skill` subcommand group on the parent program. */
|
|
53
|
+
export declare function registerSkillCommand(program: Command): void;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sweny skill` command group — discover, list, and scaffold skills.
|
|
3
|
+
*
|
|
4
|
+
* Skills are the unit of capability a workflow node names in its `skills`
|
|
5
|
+
* array. Sweny ships built-ins (github, linear, slack, ...) and discovers
|
|
6
|
+
* custom skills from `.{gemini,agents,claude,sweny}/skills/<id>/SKILL.md`.
|
|
7
|
+
*
|
|
8
|
+
* This module owns the *authoring* side of that loop:
|
|
9
|
+
*
|
|
10
|
+
* sweny skill new <id> scaffold a SKILL.md template
|
|
11
|
+
* sweny skill list list built-in + custom skills together
|
|
12
|
+
*
|
|
13
|
+
* Discovery and validation already live in `../skills/custom-loader.js`
|
|
14
|
+
* and `../skills/index.js`; this CLI is a thin authoring shell over them.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from "node:fs";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
import { builtinSkills } from "../skills/index.js";
|
|
20
|
+
import { configuredSkillsWithDiagnostics, discoverSkillsWithDiagnostics } from "../skills/custom-loader.js";
|
|
21
|
+
import { SKILL_CATEGORIES } from "../types.js";
|
|
22
|
+
// Mirror of the loader's regex so authoring-side validation matches discovery.
|
|
23
|
+
const VALID_SKILL_ID = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
24
|
+
const HARNESS_DIRS = {
|
|
25
|
+
claude: ".claude/skills",
|
|
26
|
+
sweny: ".sweny/skills",
|
|
27
|
+
agents: ".agents/skills",
|
|
28
|
+
gemini: ".gemini/skills",
|
|
29
|
+
};
|
|
30
|
+
/** Build the SKILL.md body for a fresh scaffold. Exported for tests. */
|
|
31
|
+
export function renderSkillTemplate(opts) {
|
|
32
|
+
const { id, description, category } = opts;
|
|
33
|
+
const lines = [];
|
|
34
|
+
lines.push("---");
|
|
35
|
+
lines.push(`name: ${id}`);
|
|
36
|
+
lines.push(`description: ${description}`);
|
|
37
|
+
lines.push(`category: ${category}`);
|
|
38
|
+
lines.push("# config:");
|
|
39
|
+
lines.push("# EXAMPLE_ENV_VAR:");
|
|
40
|
+
lines.push("# description: Human-readable description shown by `sweny check`");
|
|
41
|
+
lines.push("# required: true");
|
|
42
|
+
lines.push("# mcp:");
|
|
43
|
+
lines.push("# command: npx");
|
|
44
|
+
lines.push('# args: ["-y", "@your-org/your-mcp-server"]');
|
|
45
|
+
lines.push("# env:");
|
|
46
|
+
lines.push("# SOMETHING: ${SOMETHING}");
|
|
47
|
+
lines.push("---");
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push(`# ${id}`);
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push(description);
|
|
52
|
+
lines.push("");
|
|
53
|
+
lines.push("## When to use");
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push(`Reference this skill from a workflow node when the node needs ${description.toLowerCase()}.`);
|
|
56
|
+
lines.push("");
|
|
57
|
+
lines.push("```yaml");
|
|
58
|
+
lines.push("nodes:");
|
|
59
|
+
lines.push(" my_node:");
|
|
60
|
+
lines.push(" name: My Node");
|
|
61
|
+
lines.push(` instruction: >-`);
|
|
62
|
+
lines.push(` Use the ${id} capability described in this skill.`);
|
|
63
|
+
lines.push(` skills: [${id}]`);
|
|
64
|
+
lines.push("```");
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push("## What this skill provides");
|
|
67
|
+
lines.push("");
|
|
68
|
+
lines.push("Describe the contract this skill exposes. Custom skills are *instruction-only* by default — the markdown body becomes guidance the LLM sees when the node runs. Add an `mcp:` block in the frontmatter above to wire in tool execution via an MCP server.");
|
|
69
|
+
lines.push("");
|
|
70
|
+
lines.push("Concrete things to document here:");
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push("- Required environment variables (also declare in `config:` above so `sweny check` validates them).");
|
|
73
|
+
lines.push("- The exact commands or tool calls the node should make.");
|
|
74
|
+
lines.push("- Common failure modes and how to recover.");
|
|
75
|
+
lines.push("- Any preconditions the workflow must satisfy before invoking this skill.");
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push("## Example invocation");
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push("Replace this section with a concrete usage example, including the exact bash, MCP tool call, or LLM prompt the node should produce.");
|
|
80
|
+
lines.push("");
|
|
81
|
+
return lines.join("\n");
|
|
82
|
+
}
|
|
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()) {
|
|
89
|
+
const id = idArg.toLowerCase();
|
|
90
|
+
if (!VALID_SKILL_ID.test(id) || id.includes("--") || id.length > 64) {
|
|
91
|
+
console.error(chalk.red(` Invalid skill id "${idArg}".\n Skill ids must be lowercase alphanumeric + hyphens (no consecutive hyphens), max 64 chars.`));
|
|
92
|
+
process.exit(2);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const description = (options.description ?? `Custom ${id} skill`).trim();
|
|
96
|
+
const category = (options.category ?? "general");
|
|
97
|
+
if (!SKILL_CATEGORIES.includes(category)) {
|
|
98
|
+
console.error(chalk.red(` Invalid category "${category}".\n Allowed: ${SKILL_CATEGORIES.join(", ")}`));
|
|
99
|
+
process.exit(2);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const harness = options.harness ?? "claude";
|
|
103
|
+
const baseDir = HARNESS_DIRS[harness];
|
|
104
|
+
if (!baseDir) {
|
|
105
|
+
console.error(chalk.red(` Unknown harness "${harness}". Allowed: ${Object.keys(HARNESS_DIRS).join(", ")}`));
|
|
106
|
+
process.exit(2);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const skillDir = path.join(cwd, baseDir, id);
|
|
110
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
111
|
+
if (fs.existsSync(skillFile) && !options.force) {
|
|
112
|
+
console.error(chalk.red(` ${path.relative(cwd, skillFile)} already exists. Pass --force to overwrite.`));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
117
|
+
fs.writeFileSync(skillFile, renderSkillTemplate({ id, description, category }), "utf-8");
|
|
118
|
+
const rel = path.relative(cwd, skillFile);
|
|
119
|
+
console.log(chalk.green(`\n Created ${rel}\n`));
|
|
120
|
+
console.log(chalk.dim(" Next steps:"));
|
|
121
|
+
console.log(chalk.dim(` 1. Open ${rel} and replace the placeholder sections.`));
|
|
122
|
+
console.log(chalk.dim(` 2. If your skill needs env vars, uncomment the 'config:' block in the frontmatter.`));
|
|
123
|
+
console.log(chalk.dim(` 3. If your skill executes tools via MCP, uncomment the 'mcp:' block.`));
|
|
124
|
+
console.log(chalk.dim(` 4. Reference the skill in a workflow node: skills: [${id}]`));
|
|
125
|
+
console.log(chalk.dim(` 5. Run \`sweny skill list\` to confirm it discovers correctly.`));
|
|
126
|
+
console.log();
|
|
127
|
+
}
|
|
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.
|
|
137
|
+
const { skills: customSkills, warnings: customWarnings } = discoverSkillsWithDiagnostics(cwd);
|
|
138
|
+
const customIds = new Set(customSkills.map((s) => s.id));
|
|
139
|
+
const builtinList = builtinSkills.filter((s) => !customIds.has(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));
|
|
142
|
+
if (options.json) {
|
|
143
|
+
const data = [
|
|
144
|
+
...builtinList.map((s) => ({
|
|
145
|
+
id: s.id,
|
|
146
|
+
kind: "builtin",
|
|
147
|
+
category: s.category,
|
|
148
|
+
description: s.description,
|
|
149
|
+
configured: configuredIds.has(s.id),
|
|
150
|
+
})),
|
|
151
|
+
...customSkills.map((s) => ({
|
|
152
|
+
id: s.id,
|
|
153
|
+
kind: "custom",
|
|
154
|
+
category: s.category,
|
|
155
|
+
description: s.description,
|
|
156
|
+
configured: configuredIds.has(s.id),
|
|
157
|
+
})),
|
|
158
|
+
];
|
|
159
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const renderRow = (s, kind) => {
|
|
163
|
+
const tag = kind === "custom" ? chalk.magenta("custom") : chalk.dim("builtin");
|
|
164
|
+
const ok = configuredIds.has(s.id) ? chalk.green("✓") : chalk.yellow("·");
|
|
165
|
+
console.log(` ${ok} ${chalk.cyan(s.id)} ${chalk.dim(`(${s.category})`)} ${tag}`);
|
|
166
|
+
console.log(chalk.dim(` ${s.description}`));
|
|
167
|
+
};
|
|
168
|
+
console.log(chalk.bold("\nAvailable skills:\n"));
|
|
169
|
+
console.log(chalk.dim(" ✓ = configured (required env vars present)"));
|
|
170
|
+
console.log(chalk.dim(" · = present but not configured"));
|
|
171
|
+
console.log();
|
|
172
|
+
if (builtinList.length === 0 && customSkills.length === 0) {
|
|
173
|
+
console.log(chalk.dim(" (no skills found)\n"));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
for (const s of builtinList)
|
|
177
|
+
renderRow(s, "builtin");
|
|
178
|
+
for (const s of customSkills)
|
|
179
|
+
renderRow(s, "custom");
|
|
180
|
+
}
|
|
181
|
+
if (customWarnings.length > 0) {
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(chalk.yellow(` ${customWarnings.length} skill discovery warning(s):`));
|
|
184
|
+
for (const w of customWarnings) {
|
|
185
|
+
console.log(chalk.yellow(` ${w.kind}: ${w.message}`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(chalk.dim(` Scaffold a new skill: sweny skill new <id>`));
|
|
190
|
+
console.log();
|
|
191
|
+
}
|
|
192
|
+
/** Register the `sweny skill` subcommand group on the parent program. */
|
|
193
|
+
export function registerSkillCommand(program) {
|
|
194
|
+
const skillCmd = program.command("skill").description("Author and inspect skills");
|
|
195
|
+
skillCmd
|
|
196
|
+
.command("new <id>")
|
|
197
|
+
.description("Scaffold a new SKILL.md in .claude/skills/<id>/")
|
|
198
|
+
.option("-d, --description <text>", "One-line description (required field in frontmatter)")
|
|
199
|
+
.option("-c, --category <category>", `Skill category (${SKILL_CATEGORIES.join("|")})`, "general")
|
|
200
|
+
.option("--harness <harness>", `Where to place the skill (${Object.keys(HARNESS_DIRS).join("|")})`, "claude")
|
|
201
|
+
.option("--force", "Overwrite an existing SKILL.md")
|
|
202
|
+
.action((id, options) => runSkillNew(id, options));
|
|
203
|
+
skillCmd
|
|
204
|
+
.command("list")
|
|
205
|
+
.description("List built-in and custom skills together with configuration status")
|
|
206
|
+
.option("--json", "Output as JSON array")
|
|
207
|
+
.action((options) => runSkillList(options));
|
|
208
|
+
}
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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,6 +100,9 @@ function parseSkillMd(content, path) {
|
|
|
99
100
|
}
|
|
100
101
|
// Narrow the loosely-typed YAML output into the fields we expect
|
|
101
102
|
const fm = raw;
|
|
103
|
+
const category = typeof fm.category === "string" && SKILL_CATEGORIES.includes(fm.category)
|
|
104
|
+
? fm.category
|
|
105
|
+
: "general";
|
|
102
106
|
const id = String(fm.name);
|
|
103
107
|
if (!VALID_SKILL_ID.test(id) || id.includes("--") || id.length > 64) {
|
|
104
108
|
return {
|
|
@@ -145,7 +149,7 @@ function parseSkillMd(content, path) {
|
|
|
145
149
|
id,
|
|
146
150
|
name: id,
|
|
147
151
|
description: typeof fm.description === "string" ? fm.description : `Custom skill: ${id}`,
|
|
148
|
-
category
|
|
152
|
+
category,
|
|
149
153
|
config,
|
|
150
154
|
tools: [],
|
|
151
155
|
instruction: instruction || undefined,
|
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
|
-
/**
|
|
26
|
-
|
|
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 ?? ""),
|