@claude-collective/cli 0.2.0 → 0.8.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.
- package/CHANGELOG.md +178 -0
- package/README.md +1 -1
- package/dist/chunk-3HBTELJN.js +114 -0
- package/dist/chunk-3HBTELJN.js.map +1 -0
- package/dist/chunk-3ZCB5K33.js +54 -0
- package/dist/chunk-3ZCB5K33.js.map +1 -0
- package/dist/chunk-66UDJBF6.js +96 -0
- package/dist/chunk-66UDJBF6.js.map +1 -0
- package/dist/chunk-6LS7XO3H.js +31 -0
- package/dist/chunk-6LS7XO3H.js.map +1 -0
- package/dist/chunk-A3J6IAXK.js +57 -0
- package/dist/chunk-A3J6IAXK.js.map +1 -0
- package/dist/chunk-A65SBAAJ.js +69 -0
- package/dist/chunk-A65SBAAJ.js.map +1 -0
- package/dist/chunk-ALEPJ6YN.js +80 -0
- package/dist/chunk-ALEPJ6YN.js.map +1 -0
- package/dist/chunk-C4ZTIYFR.js +84 -0
- package/dist/chunk-C4ZTIYFR.js.map +1 -0
- package/dist/chunk-CIY5UBRB.js +453 -0
- package/dist/chunk-CIY5UBRB.js.map +1 -0
- package/dist/chunk-DHET7RCE.js +50 -0
- package/dist/chunk-DHET7RCE.js.map +1 -0
- package/dist/chunk-DHFFRMF6.js +31 -0
- package/dist/chunk-DHFFRMF6.js.map +1 -0
- package/dist/chunk-DKGL77IY.js +307 -0
- package/dist/chunk-DKGL77IY.js.map +1 -0
- package/dist/chunk-ED73HCW2.js +315 -0
- package/dist/chunk-ED73HCW2.js.map +1 -0
- package/dist/chunk-FNOYEXUE.js +308 -0
- package/dist/chunk-FNOYEXUE.js.map +1 -0
- package/dist/chunk-G2FBJOZG.js +141 -0
- package/dist/chunk-G2FBJOZG.js.map +1 -0
- package/dist/chunk-HNDT5QRB.js +120 -0
- package/dist/chunk-HNDT5QRB.js.map +1 -0
- package/dist/chunk-K7PTOVX4.js +158 -0
- package/dist/chunk-K7PTOVX4.js.map +1 -0
- package/dist/chunk-LQTST4WY.js +91 -0
- package/dist/chunk-LQTST4WY.js.map +1 -0
- package/dist/chunk-LVKRVFYR.js +54 -0
- package/dist/chunk-LVKRVFYR.js.map +1 -0
- package/dist/chunk-M7YCPFIX.js +108 -0
- package/dist/chunk-M7YCPFIX.js.map +1 -0
- package/dist/chunk-MJSFR562.js +57 -0
- package/dist/chunk-MJSFR562.js.map +1 -0
- package/dist/chunk-MMDXNZPF.js +69 -0
- package/dist/chunk-MMDXNZPF.js.map +1 -0
- package/dist/chunk-MYAVQ23U.js +356 -0
- package/dist/chunk-MYAVQ23U.js.map +1 -0
- package/dist/chunk-NGBFJJ7Q.js +124 -0
- package/dist/chunk-NGBFJJ7Q.js.map +1 -0
- package/dist/chunk-OLBOTK3O.js +64 -0
- package/dist/chunk-OLBOTK3O.js.map +1 -0
- package/dist/chunk-PPNTD5LO.js +330 -0
- package/dist/chunk-PPNTD5LO.js.map +1 -0
- package/dist/chunk-Q2LH2DAB.js +392 -0
- package/dist/chunk-Q2LH2DAB.js.map +1 -0
- package/dist/chunk-Q6DR5QUH.js +547 -0
- package/dist/chunk-Q6DR5QUH.js.map +1 -0
- package/dist/chunk-QESUUPOE.js +241 -0
- package/dist/chunk-QESUUPOE.js.map +1 -0
- package/dist/chunk-QGGSLMO3.js +607 -0
- package/dist/chunk-QGGSLMO3.js.map +1 -0
- package/dist/chunk-SEBPPFUW.js +478 -0
- package/dist/chunk-SEBPPFUW.js.map +1 -0
- package/dist/chunk-SYQ7R2JO.js +95 -0
- package/dist/chunk-SYQ7R2JO.js.map +1 -0
- package/dist/chunk-TOPAIL5W.js +22 -0
- package/dist/chunk-TOPAIL5W.js.map +1 -0
- package/dist/chunk-U4VYHKPM.js +110 -0
- package/dist/chunk-U4VYHKPM.js.map +1 -0
- package/dist/chunk-UOWHJ6BE.js +83 -0
- package/dist/chunk-UOWHJ6BE.js.map +1 -0
- package/dist/chunk-XKEG3SCV.js +86 -0
- package/dist/chunk-XKEG3SCV.js.map +1 -0
- package/dist/chunk-XY3XDVMI.js +15599 -0
- package/dist/chunk-XY3XDVMI.js.map +1 -0
- package/dist/chunk-Y3V43XCU.js +76 -0
- package/dist/chunk-Y3V43XCU.js.map +1 -0
- package/dist/chunk-YKXBGCFD.js +129 -0
- package/dist/chunk-YKXBGCFD.js.map +1 -0
- package/dist/cli-v2/defaults/agent-mappings.yaml +185 -0
- package/dist/commands/build/marketplace.js +254 -0
- package/dist/commands/build/marketplace.js.map +1 -0
- package/dist/commands/build/plugins.js +324 -0
- package/dist/commands/build/plugins.js.map +1 -0
- package/dist/commands/build/stack.js +169 -0
- package/dist/commands/build/stack.js.map +1 -0
- package/dist/commands/compile.js +461 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/config/get.js +60 -0
- package/dist/commands/config/get.js.map +1 -0
- package/dist/commands/config/index.js +22 -0
- package/dist/commands/config/index.js.map +1 -0
- package/dist/commands/config/path.js +35 -0
- package/dist/commands/config/path.js.map +1 -0
- package/dist/commands/config/set-project.js +61 -0
- package/dist/commands/config/set-project.js.map +1 -0
- package/dist/commands/config/set.js +60 -0
- package/dist/commands/config/set.js.map +1 -0
- package/dist/commands/config/show.js +13 -0
- package/dist/commands/config/show.js.map +1 -0
- package/dist/commands/config/unset-project.js +57 -0
- package/dist/commands/config/unset-project.js.map +1 -0
- package/dist/commands/config/unset.js +56 -0
- package/dist/commands/config/unset.js.map +1 -0
- package/dist/commands/diff.js +755 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit.js +254 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/eject.js +208 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/info.js +205 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.js +915 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.js +44 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new/agent.js +230 -0
- package/dist/commands/new/agent.js.map +1 -0
- package/dist/commands/new/skill.js +204 -0
- package/dist/commands/new/skill.js.map +1 -0
- package/dist/commands/outdated.js +242 -0
- package/dist/commands/outdated.js.map +1 -0
- package/dist/commands/search.js +115 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/test-imports.js +92 -0
- package/dist/commands/test-imports.js.map +1 -0
- package/dist/commands/uninstall.js +309 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +428 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.js +375 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/version/bump.js +95 -0
- package/dist/commands/version/bump.js.map +1 -0
- package/dist/commands/version/index.js +70 -0
- package/dist/commands/version/index.js.map +1 -0
- package/dist/commands/version/set.js +101 -0
- package/dist/commands/version/set.js.map +1 -0
- package/dist/commands/version/show.js +70 -0
- package/dist/commands/version/show.js.map +1 -0
- package/dist/components/common/confirm.js +9 -0
- package/dist/components/common/confirm.js.map +1 -0
- package/dist/components/common/message.js +24 -0
- package/dist/components/common/message.js.map +1 -0
- package/dist/components/common/spinner.js +14 -0
- package/dist/components/common/spinner.js.map +1 -0
- package/dist/components/wizard/category-grid.js +9 -0
- package/dist/components/wizard/category-grid.js.map +1 -0
- package/dist/components/wizard/category-grid.test.js +728 -0
- package/dist/components/wizard/category-grid.test.js.map +1 -0
- package/dist/components/wizard/section-progress.js +9 -0
- package/dist/components/wizard/section-progress.js.map +1 -0
- package/dist/components/wizard/section-progress.test.js +281 -0
- package/dist/components/wizard/section-progress.test.js.map +1 -0
- package/dist/components/wizard/step-approach.js +11 -0
- package/dist/components/wizard/step-approach.js.map +1 -0
- package/dist/components/wizard/step-build.js +15 -0
- package/dist/components/wizard/step-build.js.map +1 -0
- package/dist/components/wizard/step-build.test.js +729 -0
- package/dist/components/wizard/step-build.test.js.map +1 -0
- package/dist/components/wizard/step-confirm.js +9 -0
- package/dist/components/wizard/step-confirm.js.map +1 -0
- package/dist/components/wizard/step-refine.js +9 -0
- package/dist/components/wizard/step-refine.js.map +1 -0
- package/dist/components/wizard/step-refine.test.js +235 -0
- package/dist/components/wizard/step-refine.test.js.map +1 -0
- package/dist/components/wizard/step-stack-options.js +11 -0
- package/dist/components/wizard/step-stack-options.js.map +1 -0
- package/dist/components/wizard/step-stack.js +11 -0
- package/dist/components/wizard/step-stack.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +11 -0
- package/dist/components/wizard/wizard-tabs.js.map +1 -0
- package/dist/components/wizard/wizard.js +20 -0
- package/dist/components/wizard/wizard.js.map +1 -0
- package/dist/hooks/init.js +41 -0
- package/dist/hooks/init.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-string.es-RGXYGAW3.js +1316 -0
- package/dist/magic-string.es-RGXYGAW3.js.map +1 -0
- package/dist/stores/wizard-store.js +10 -0
- package/dist/stores/wizard-store.js.map +1 -0
- package/dist/stores/wizard-store.test.js +405 -0
- package/dist/stores/wizard-store.test.js.map +1 -0
- package/package.json +44 -25
- package/dist/cli/index.js +0 -6314
- package/dist/cli/index.js.map +0 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadGlobalConfig
|
|
4
|
+
} from "../../chunk-QESUUPOE.js";
|
|
5
|
+
import {
|
|
6
|
+
LOCAL_SKILLS_PATH
|
|
7
|
+
} from "../../chunk-A3J6IAXK.js";
|
|
8
|
+
import {
|
|
9
|
+
BaseCommand,
|
|
10
|
+
EXIT_CODES
|
|
11
|
+
} from "../../chunk-SYQ7R2JO.js";
|
|
12
|
+
import "../../chunk-TOPAIL5W.js";
|
|
13
|
+
import {
|
|
14
|
+
directoryExists,
|
|
15
|
+
writeFile
|
|
16
|
+
} from "../../chunk-MMDXNZPF.js";
|
|
17
|
+
import {
|
|
18
|
+
init_esm_shims
|
|
19
|
+
} from "../../chunk-DHET7RCE.js";
|
|
20
|
+
|
|
21
|
+
// src/cli-v2/commands/new/skill.ts
|
|
22
|
+
init_esm_shims();
|
|
23
|
+
import { Args, Flags } from "@oclif/core";
|
|
24
|
+
import path from "path";
|
|
25
|
+
var DEFAULT_AUTHOR = "@local";
|
|
26
|
+
var DEFAULT_CATEGORY = "local";
|
|
27
|
+
var KEBAB_CASE_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
28
|
+
function validateSkillName(name) {
|
|
29
|
+
if (!name || name.trim() === "") {
|
|
30
|
+
return "Skill name is required";
|
|
31
|
+
}
|
|
32
|
+
if (!KEBAB_CASE_PATTERN.test(name)) {
|
|
33
|
+
return "Skill name must be kebab-case (lowercase letters, numbers, and hyphens, starting with a letter)";
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function toTitleCase(kebabCase) {
|
|
38
|
+
return kebabCase.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
39
|
+
}
|
|
40
|
+
function generateSkillMd(name, author) {
|
|
41
|
+
const titleName = toTitleCase(name);
|
|
42
|
+
const skillId = `${name} (${author})`;
|
|
43
|
+
return `---
|
|
44
|
+
name: ${skillId}
|
|
45
|
+
description: Brief description of this skill
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
# ${titleName}
|
|
49
|
+
|
|
50
|
+
> **Quick Guide:** Add a brief summary of what this skill teaches.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<critical_requirements>
|
|
55
|
+
|
|
56
|
+
## CRITICAL: Before Using This Skill
|
|
57
|
+
|
|
58
|
+
**(Add critical requirements here)**
|
|
59
|
+
|
|
60
|
+
</critical_requirements>
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
**When to use:**
|
|
65
|
+
|
|
66
|
+
- Add use cases here
|
|
67
|
+
|
|
68
|
+
**Key patterns covered:**
|
|
69
|
+
|
|
70
|
+
- Add patterns here
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
<patterns>
|
|
75
|
+
|
|
76
|
+
## Core Patterns
|
|
77
|
+
|
|
78
|
+
### Pattern 1: Example Pattern
|
|
79
|
+
|
|
80
|
+
Add your patterns here.
|
|
81
|
+
|
|
82
|
+
</patterns>
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
<critical_reminders>
|
|
87
|
+
|
|
88
|
+
## CRITICAL REMINDERS
|
|
89
|
+
|
|
90
|
+
**(Repeat critical requirements here)**
|
|
91
|
+
|
|
92
|
+
</critical_reminders>
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
function generateMetadataYaml(name, author, category) {
|
|
96
|
+
const titleName = toTitleCase(name);
|
|
97
|
+
return `# yaml-language-server: $schema=https://raw.githubusercontent.com/claude-collective/skills/main/schemas/metadata.schema.json
|
|
98
|
+
category: ${category}
|
|
99
|
+
category_exclusive: false
|
|
100
|
+
author: "${author}"
|
|
101
|
+
version: 1
|
|
102
|
+
cli_name: ${titleName}
|
|
103
|
+
cli_description: Brief description
|
|
104
|
+
usage_guidance: Use when <guidance>.
|
|
105
|
+
tags:
|
|
106
|
+
- local
|
|
107
|
+
- custom
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
var NewSkill = class _NewSkill extends BaseCommand {
|
|
111
|
+
static summary = "Create a new local skill with proper structure";
|
|
112
|
+
static description = "Create a new local skill scaffold with SKILL.md and metadata.yaml files";
|
|
113
|
+
static args = {
|
|
114
|
+
name: Args.string({
|
|
115
|
+
description: "Name of the skill to create (kebab-case)",
|
|
116
|
+
required: true
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
static flags = {
|
|
120
|
+
...BaseCommand.baseFlags,
|
|
121
|
+
author: Flags.string({
|
|
122
|
+
char: "a",
|
|
123
|
+
description: "Author identifier (e.g., @myhandle)",
|
|
124
|
+
required: false
|
|
125
|
+
}),
|
|
126
|
+
category: Flags.string({
|
|
127
|
+
char: "c",
|
|
128
|
+
description: "Skill category",
|
|
129
|
+
default: DEFAULT_CATEGORY
|
|
130
|
+
}),
|
|
131
|
+
force: Flags.boolean({
|
|
132
|
+
char: "f",
|
|
133
|
+
description: "Overwrite existing skill directory",
|
|
134
|
+
default: false
|
|
135
|
+
})
|
|
136
|
+
};
|
|
137
|
+
async run() {
|
|
138
|
+
const { args, flags } = await this.parse(_NewSkill);
|
|
139
|
+
const projectDir = process.cwd();
|
|
140
|
+
this.log("");
|
|
141
|
+
this.log("Create New Skill");
|
|
142
|
+
this.log("");
|
|
143
|
+
const validationError = validateSkillName(args.name);
|
|
144
|
+
if (validationError) {
|
|
145
|
+
this.error(validationError, { exit: EXIT_CODES.INVALID_ARGS });
|
|
146
|
+
}
|
|
147
|
+
let author = flags.author;
|
|
148
|
+
if (!author) {
|
|
149
|
+
const globalConfig = await loadGlobalConfig();
|
|
150
|
+
author = globalConfig?.author || DEFAULT_AUTHOR;
|
|
151
|
+
}
|
|
152
|
+
const category = flags.category;
|
|
153
|
+
const skillDir = path.join(projectDir, LOCAL_SKILLS_PATH, args.name);
|
|
154
|
+
if (await directoryExists(skillDir)) {
|
|
155
|
+
if (!flags.force) {
|
|
156
|
+
this.error(
|
|
157
|
+
`Skill directory already exists: ${skillDir}
|
|
158
|
+
Use --force to overwrite.`,
|
|
159
|
+
{ exit: EXIT_CODES.ERROR }
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
this.warn(`Overwriting existing skill at ${skillDir}`);
|
|
163
|
+
}
|
|
164
|
+
this.log(`Skill name: ${args.name}`);
|
|
165
|
+
this.log(`Author: ${author}`);
|
|
166
|
+
this.log(`Category: ${category}`);
|
|
167
|
+
this.log(`Directory: ${skillDir}`);
|
|
168
|
+
this.log("");
|
|
169
|
+
if (flags["dry-run"]) {
|
|
170
|
+
this.log("[DRY RUN] Would create skill files");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.log("Creating skill files...");
|
|
174
|
+
try {
|
|
175
|
+
const skillMdContent = generateSkillMd(args.name, author);
|
|
176
|
+
const metadataContent = generateMetadataYaml(args.name, author, category);
|
|
177
|
+
const skillMdPath = path.join(skillDir, "SKILL.md");
|
|
178
|
+
const metadataPath = path.join(skillDir, "metadata.yaml");
|
|
179
|
+
await writeFile(skillMdPath, skillMdContent);
|
|
180
|
+
await writeFile(metadataPath, metadataContent);
|
|
181
|
+
this.log("");
|
|
182
|
+
this.logSuccess(`Created SKILL.md at ${skillMdPath}`);
|
|
183
|
+
this.logSuccess(`Created metadata.yaml at ${metadataPath}`);
|
|
184
|
+
this.log("");
|
|
185
|
+
this.log(
|
|
186
|
+
"Skill created successfully! Run 'cc compile' to include it in your agents."
|
|
187
|
+
);
|
|
188
|
+
this.log("");
|
|
189
|
+
} catch (error) {
|
|
190
|
+
this.error(
|
|
191
|
+
error instanceof Error ? error.message : "Unknown error occurred",
|
|
192
|
+
{ exit: EXIT_CODES.ERROR }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
export {
|
|
198
|
+
NewSkill as default,
|
|
199
|
+
generateMetadataYaml,
|
|
200
|
+
generateSkillMd,
|
|
201
|
+
toTitleCase,
|
|
202
|
+
validateSkillName
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli-v2/commands/new/skill.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport path from \"path\";\nimport { BaseCommand } from \"../../base-command.js\";\nimport { loadGlobalConfig } from \"../../lib/config.js\";\nimport { writeFile, directoryExists } from \"../../utils/fs.js\";\nimport { LOCAL_SKILLS_PATH } from \"../../consts.js\";\nimport { EXIT_CODES } from \"../../lib/exit-codes.js\";\n\nconst DEFAULT_AUTHOR = \"@local\";\nconst DEFAULT_CATEGORY = \"local\";\nconst KEBAB_CASE_PATTERN = /^[a-z][a-z0-9-]*$/;\n\n/**\n * Validates that a skill name follows kebab-case convention.\n * Must start with lowercase letter, followed by lowercase letters, numbers, or hyphens.\n */\nexport function validateSkillName(name: string): string | null {\n if (!name || name.trim() === \"\") {\n return \"Skill name is required\";\n }\n\n if (!KEBAB_CASE_PATTERN.test(name)) {\n return \"Skill name must be kebab-case (lowercase letters, numbers, and hyphens, starting with a letter)\";\n }\n\n return null;\n}\n\n/**\n * Converts kebab-case to Title Case.\n * e.g., \"my-patterns\" -> \"My Patterns\"\n */\nexport function toTitleCase(kebabCase: string): string {\n return kebabCase\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/**\n * Generates the SKILL.md content with frontmatter.\n */\nexport function generateSkillMd(name: string, author: string): string {\n const titleName = toTitleCase(name);\n const skillId = `${name} (${author})`;\n\n return `---\nname: ${skillId}\ndescription: Brief description of this skill\n---\n\n# ${titleName}\n\n> **Quick Guide:** Add a brief summary of what this skill teaches.\n\n---\n\n<critical_requirements>\n\n## CRITICAL: Before Using This Skill\n\n**(Add critical requirements here)**\n\n</critical_requirements>\n\n---\n\n**When to use:**\n\n- Add use cases here\n\n**Key patterns covered:**\n\n- Add patterns here\n\n---\n\n<patterns>\n\n## Core Patterns\n\n### Pattern 1: Example Pattern\n\nAdd your patterns here.\n\n</patterns>\n\n---\n\n<critical_reminders>\n\n## CRITICAL REMINDERS\n\n**(Repeat critical requirements here)**\n\n</critical_reminders>\n`;\n}\n\n/**\n * Generates the metadata.yaml content.\n */\nexport function generateMetadataYaml(\n name: string,\n author: string,\n category: string,\n): string {\n const titleName = toTitleCase(name);\n\n return `# yaml-language-server: $schema=https://raw.githubusercontent.com/claude-collective/skills/main/schemas/metadata.schema.json\ncategory: ${category}\ncategory_exclusive: false\nauthor: \"${author}\"\nversion: 1\ncli_name: ${titleName}\ncli_description: Brief description\nusage_guidance: Use when <guidance>.\ntags:\n - local\n - custom\n`;\n}\n\nexport default class NewSkill extends BaseCommand {\n static summary = \"Create a new local skill with proper structure\";\n static description =\n \"Create a new local skill scaffold with SKILL.md and metadata.yaml files\";\n\n static args = {\n name: Args.string({\n description: \"Name of the skill to create (kebab-case)\",\n required: true,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n author: Flags.string({\n char: \"a\",\n description: \"Author identifier (e.g., @myhandle)\",\n required: false,\n }),\n category: Flags.string({\n char: \"c\",\n description: \"Skill category\",\n default: DEFAULT_CATEGORY,\n }),\n force: Flags.boolean({\n char: \"f\",\n description: \"Overwrite existing skill directory\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(NewSkill);\n const projectDir = process.cwd();\n\n this.log(\"\");\n this.log(\"Create New Skill\");\n this.log(\"\");\n\n // Validate skill name\n const validationError = validateSkillName(args.name);\n if (validationError) {\n this.error(validationError, { exit: EXIT_CODES.INVALID_ARGS });\n }\n\n // Determine author: flag > global config > default\n let author = flags.author;\n if (!author) {\n const globalConfig = await loadGlobalConfig();\n author = globalConfig?.author || DEFAULT_AUTHOR;\n }\n\n const category = flags.category;\n\n // Determine skill directory path\n const skillDir = path.join(projectDir, LOCAL_SKILLS_PATH, args.name);\n\n // Check if directory already exists\n if (await directoryExists(skillDir)) {\n if (!flags.force) {\n this.error(\n `Skill directory already exists: ${skillDir}\\nUse --force to overwrite.`,\n { exit: EXIT_CODES.ERROR },\n );\n }\n this.warn(`Overwriting existing skill at ${skillDir}`);\n }\n\n this.log(`Skill name: ${args.name}`);\n this.log(`Author: ${author}`);\n this.log(`Category: ${category}`);\n this.log(`Directory: ${skillDir}`);\n this.log(\"\");\n\n if (flags[\"dry-run\"]) {\n this.log(\"[DRY RUN] Would create skill files\");\n return;\n }\n\n this.log(\"Creating skill files...\");\n\n try {\n // Generate file contents\n const skillMdContent = generateSkillMd(args.name, author);\n const metadataContent = generateMetadataYaml(args.name, author, category);\n\n // Write files (writeFile automatically creates parent directories)\n const skillMdPath = path.join(skillDir, \"SKILL.md\");\n const metadataPath = path.join(skillDir, \"metadata.yaml\");\n\n await writeFile(skillMdPath, skillMdContent);\n await writeFile(metadataPath, metadataContent);\n\n this.log(\"\");\n this.logSuccess(`Created SKILL.md at ${skillMdPath}`);\n this.logSuccess(`Created metadata.yaml at ${metadataPath}`);\n this.log(\"\");\n this.log(\n \"Skill created successfully! Run 'cc compile' to include it in your agents.\",\n );\n this.log(\"\");\n } catch (error) {\n this.error(\n error instanceof Error ? error.message : \"Unknown error occurred\",\n { exit: EXIT_CODES.ERROR },\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
hashFile
|
|
4
|
+
} from "../chunk-MJSFR562.js";
|
|
5
|
+
import {
|
|
6
|
+
loadSkillsMatrixFromSource
|
|
7
|
+
} from "../chunk-DKGL77IY.js";
|
|
8
|
+
import "../chunk-QGGSLMO3.js";
|
|
9
|
+
import "../chunk-NGBFJJ7Q.js";
|
|
10
|
+
import "../chunk-QESUUPOE.js";
|
|
11
|
+
import {
|
|
12
|
+
LOCAL_SKILLS_PATH
|
|
13
|
+
} from "../chunk-A3J6IAXK.js";
|
|
14
|
+
import {
|
|
15
|
+
BaseCommand,
|
|
16
|
+
EXIT_CODES
|
|
17
|
+
} from "../chunk-SYQ7R2JO.js";
|
|
18
|
+
import "../chunk-TOPAIL5W.js";
|
|
19
|
+
import {
|
|
20
|
+
fileExists,
|
|
21
|
+
listDirectories,
|
|
22
|
+
readFile
|
|
23
|
+
} from "../chunk-MMDXNZPF.js";
|
|
24
|
+
import {
|
|
25
|
+
init_esm_shims
|
|
26
|
+
} from "../chunk-DHET7RCE.js";
|
|
27
|
+
|
|
28
|
+
// src/cli-v2/commands/outdated.ts
|
|
29
|
+
init_esm_shims();
|
|
30
|
+
import { Flags } from "@oclif/core";
|
|
31
|
+
import { printTable } from "@oclif/table";
|
|
32
|
+
import path from "path";
|
|
33
|
+
import { parse as parseYaml } from "yaml";
|
|
34
|
+
async function readForkedFromMetadata(skillDir) {
|
|
35
|
+
const metadataPath = path.join(skillDir, "metadata.yaml");
|
|
36
|
+
if (!await fileExists(metadataPath)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const content = await readFile(metadataPath);
|
|
40
|
+
const metadata = parseYaml(content);
|
|
41
|
+
return metadata.forked_from ?? null;
|
|
42
|
+
}
|
|
43
|
+
async function getLocalSkillsWithMetadata(projectDir) {
|
|
44
|
+
const localSkillsPath = path.join(projectDir, LOCAL_SKILLS_PATH);
|
|
45
|
+
const result = /* @__PURE__ */ new Map();
|
|
46
|
+
if (!await fileExists(localSkillsPath)) {
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
const skillDirs = await listDirectories(localSkillsPath);
|
|
50
|
+
for (const dirName of skillDirs) {
|
|
51
|
+
const skillDir = path.join(localSkillsPath, dirName);
|
|
52
|
+
const forkedFrom = await readForkedFromMetadata(skillDir);
|
|
53
|
+
const skillId = forkedFrom?.skill_id ?? dirName;
|
|
54
|
+
result.set(skillId, { dirName, forkedFrom });
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
async function computeSourceHash(sourcePath, skillPath) {
|
|
59
|
+
const skillMdPath = path.join(sourcePath, "src", skillPath, "SKILL.md");
|
|
60
|
+
if (!await fileExists(skillMdPath)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return hashFile(skillMdPath);
|
|
64
|
+
}
|
|
65
|
+
async function compareSkills(projectDir, sourcePath, sourceSkills) {
|
|
66
|
+
const results = [];
|
|
67
|
+
const localSkills = await getLocalSkillsWithMetadata(projectDir);
|
|
68
|
+
for (const [skillId, { forkedFrom }] of localSkills) {
|
|
69
|
+
if (!forkedFrom) {
|
|
70
|
+
results.push({
|
|
71
|
+
id: skillId,
|
|
72
|
+
localHash: null,
|
|
73
|
+
sourceHash: null,
|
|
74
|
+
status: "local-only"
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const localHash = forkedFrom.content_hash;
|
|
79
|
+
const sourceSkill = sourceSkills[forkedFrom.skill_id];
|
|
80
|
+
if (!sourceSkill) {
|
|
81
|
+
results.push({
|
|
82
|
+
id: forkedFrom.skill_id,
|
|
83
|
+
localHash,
|
|
84
|
+
sourceHash: null,
|
|
85
|
+
status: "local-only"
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const sourceHash = await computeSourceHash(sourcePath, sourceSkill.path);
|
|
90
|
+
if (sourceHash === null) {
|
|
91
|
+
results.push({
|
|
92
|
+
id: forkedFrom.skill_id,
|
|
93
|
+
localHash,
|
|
94
|
+
sourceHash: null,
|
|
95
|
+
status: "local-only"
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const status = localHash === sourceHash ? "current" : "outdated";
|
|
100
|
+
results.push({
|
|
101
|
+
id: forkedFrom.skill_id,
|
|
102
|
+
localHash,
|
|
103
|
+
sourceHash,
|
|
104
|
+
status
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
results.sort((a, b) => a.id.localeCompare(b.id));
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
function calculateSummary(results) {
|
|
111
|
+
return {
|
|
112
|
+
outdated: results.filter((r) => r.status === "outdated").length,
|
|
113
|
+
current: results.filter((r) => r.status === "current").length,
|
|
114
|
+
localOnly: results.filter((r) => r.status === "local-only").length
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function formatHash(hash, isLocal) {
|
|
118
|
+
if (hash === null) {
|
|
119
|
+
return isLocal ? "(local)" : "-";
|
|
120
|
+
}
|
|
121
|
+
return hash;
|
|
122
|
+
}
|
|
123
|
+
var Outdated = class _Outdated extends BaseCommand {
|
|
124
|
+
static summary = "Check which local skills are out of date compared to source";
|
|
125
|
+
static description = "Compare local skills against their source repository to identify outdated skills that need updating";
|
|
126
|
+
static flags = {
|
|
127
|
+
...BaseCommand.baseFlags,
|
|
128
|
+
json: Flags.boolean({
|
|
129
|
+
description: "Output results as JSON",
|
|
130
|
+
default: false
|
|
131
|
+
})
|
|
132
|
+
};
|
|
133
|
+
async run() {
|
|
134
|
+
const { flags } = await this.parse(_Outdated);
|
|
135
|
+
const projectDir = process.cwd();
|
|
136
|
+
try {
|
|
137
|
+
const localSkillsPath = path.join(projectDir, LOCAL_SKILLS_PATH);
|
|
138
|
+
if (!await fileExists(localSkillsPath)) {
|
|
139
|
+
if (flags.json) {
|
|
140
|
+
this.log(
|
|
141
|
+
JSON.stringify({
|
|
142
|
+
skills: [],
|
|
143
|
+
summary: { outdated: 0, current: 0, localOnly: 0 }
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
this.warn("No local skills found. Run `cc init` or `cc edit` first.");
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (!flags.json) {
|
|
152
|
+
this.log("Loading skills...");
|
|
153
|
+
}
|
|
154
|
+
const { matrix, sourcePath, isLocal } = await loadSkillsMatrixFromSource({
|
|
155
|
+
sourceFlag: flags.source,
|
|
156
|
+
projectDir
|
|
157
|
+
});
|
|
158
|
+
if (!flags.json) {
|
|
159
|
+
this.log(`Loaded from ${isLocal ? "local" : "remote"}: ${sourcePath}`);
|
|
160
|
+
}
|
|
161
|
+
const sourceSkills = {};
|
|
162
|
+
for (const [skillId, skill] of Object.entries(matrix.skills)) {
|
|
163
|
+
if (!skill.local) {
|
|
164
|
+
sourceSkills[skillId] = { path: skill.path };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const results = await compareSkills(projectDir, sourcePath, sourceSkills);
|
|
168
|
+
const summary = calculateSummary(results);
|
|
169
|
+
if (flags.json) {
|
|
170
|
+
this.log(
|
|
171
|
+
JSON.stringify(
|
|
172
|
+
{
|
|
173
|
+
skills: results.map((r) => ({
|
|
174
|
+
id: r.id,
|
|
175
|
+
localHash: r.localHash,
|
|
176
|
+
sourceHash: r.sourceHash,
|
|
177
|
+
status: r.status
|
|
178
|
+
})),
|
|
179
|
+
summary: {
|
|
180
|
+
outdated: summary.outdated,
|
|
181
|
+
current: summary.current,
|
|
182
|
+
localOnly: summary.localOnly
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
null,
|
|
186
|
+
2
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
this.log("");
|
|
191
|
+
if (results.length === 0) {
|
|
192
|
+
this.logInfo("No local skills found to compare.");
|
|
193
|
+
} else {
|
|
194
|
+
printTable({
|
|
195
|
+
data: results.map((result) => ({
|
|
196
|
+
skill: result.id,
|
|
197
|
+
localHash: formatHash(result.localHash, true),
|
|
198
|
+
sourceHash: formatHash(result.sourceHash, false),
|
|
199
|
+
status: result.status
|
|
200
|
+
})),
|
|
201
|
+
columns: [
|
|
202
|
+
{ key: "skill", name: "Skill" },
|
|
203
|
+
{ key: "localHash", name: "Local Hash" },
|
|
204
|
+
{ key: "sourceHash", name: "Source Hash" },
|
|
205
|
+
{ key: "status", name: "Status" }
|
|
206
|
+
],
|
|
207
|
+
headerOptions: { bold: true }
|
|
208
|
+
});
|
|
209
|
+
this.log("");
|
|
210
|
+
const parts = [];
|
|
211
|
+
if (summary.outdated > 0) {
|
|
212
|
+
parts.push(`${summary.outdated} outdated`);
|
|
213
|
+
}
|
|
214
|
+
if (summary.current > 0) {
|
|
215
|
+
parts.push(`${summary.current} current`);
|
|
216
|
+
}
|
|
217
|
+
if (summary.localOnly > 0) {
|
|
218
|
+
parts.push(`${summary.localOnly} local-only`);
|
|
219
|
+
}
|
|
220
|
+
this.log(`Summary: ${parts.join(", ")}`);
|
|
221
|
+
}
|
|
222
|
+
this.log("");
|
|
223
|
+
}
|
|
224
|
+
if (summary.outdated > 0) {
|
|
225
|
+
this.error("Some skills are outdated", { exit: EXIT_CODES.ERROR });
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
229
|
+
if (flags.json) {
|
|
230
|
+
this.error(JSON.stringify({ error: message }), {
|
|
231
|
+
exit: EXIT_CODES.ERROR
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
this.error(`Error: ${message}`, { exit: EXIT_CODES.ERROR });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
export {
|
|
240
|
+
Outdated as default
|
|
241
|
+
};
|
|
242
|
+
//# sourceMappingURL=outdated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli-v2/commands/outdated.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { printTable } from \"@oclif/table\";\nimport path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { loadSkillsMatrixFromSource } from \"../lib/source-loader.js\";\nimport { hashFile } from \"../lib/versioning.js\";\nimport { fileExists, readFile, listDirectories } from \"../utils/fs.js\";\nimport { LOCAL_SKILLS_PATH } from \"../consts.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\n\n/**\n * Status values for skill comparison\n */\ntype SkillStatus = \"current\" | \"outdated\" | \"local-only\";\n\n/**\n * Result of comparing a local skill to its source\n */\ninterface SkillComparisonResult {\n id: string;\n localHash: string | null;\n sourceHash: string | null;\n status: SkillStatus;\n}\n\n/**\n * Summary counts for the comparison results\n */\ninterface ComparisonSummary {\n outdated: number;\n current: number;\n localOnly: number;\n}\n\n/**\n * ForkedFrom metadata stored in local skill's metadata.yaml\n */\ninterface ForkedFromMetadata {\n skill_id: string;\n content_hash: string;\n date: string;\n}\n\n/**\n * Local skill metadata structure\n */\ninterface LocalSkillMetadata {\n forked_from?: ForkedFromMetadata;\n [key: string]: unknown;\n}\n\n/**\n * Read forked_from metadata from a local skill's metadata.yaml\n */\nasync function readForkedFromMetadata(\n skillDir: string,\n): Promise<ForkedFromMetadata | null> {\n const metadataPath = path.join(skillDir, \"metadata.yaml\");\n\n if (!(await fileExists(metadataPath))) {\n return null;\n }\n\n const content = await readFile(metadataPath);\n const metadata = parseYaml(content) as LocalSkillMetadata;\n\n return metadata.forked_from ?? null;\n}\n\n/**\n * Get local skills with their forked_from metadata\n */\nasync function getLocalSkillsWithMetadata(\n projectDir: string,\n): Promise<\n Map<string, { dirName: string; forkedFrom: ForkedFromMetadata | null }>\n> {\n const localSkillsPath = path.join(projectDir, LOCAL_SKILLS_PATH);\n const result = new Map<\n string,\n { dirName: string; forkedFrom: ForkedFromMetadata | null }\n >();\n\n if (!(await fileExists(localSkillsPath))) {\n return result;\n }\n\n const skillDirs = await listDirectories(localSkillsPath);\n\n for (const dirName of skillDirs) {\n const skillDir = path.join(localSkillsPath, dirName);\n const forkedFrom = await readForkedFromMetadata(skillDir);\n\n // Use the skill_id from forked_from if available, otherwise use directory name\n const skillId = forkedFrom?.skill_id ?? dirName;\n\n result.set(skillId, { dirName, forkedFrom });\n }\n\n return result;\n}\n\n/**\n * Compute source hash for a skill's SKILL.md file\n */\nasync function computeSourceHash(\n sourcePath: string,\n skillPath: string,\n): Promise<string | null> {\n const skillMdPath = path.join(sourcePath, \"src\", skillPath, \"SKILL.md\");\n\n if (!(await fileExists(skillMdPath))) {\n return null;\n }\n\n return hashFile(skillMdPath);\n}\n\n/**\n * Compare local skills against source and determine status\n */\nasync function compareSkills(\n projectDir: string,\n sourcePath: string,\n sourceSkills: Record<string, { path: string }>,\n): Promise<SkillComparisonResult[]> {\n const results: SkillComparisonResult[] = [];\n const localSkills = await getLocalSkillsWithMetadata(projectDir);\n\n // Process local skills\n for (const [skillId, { forkedFrom }] of localSkills) {\n if (!forkedFrom) {\n // Local-only skill (no forked_from metadata)\n results.push({\n id: skillId,\n localHash: null,\n sourceHash: null,\n status: \"local-only\",\n });\n continue;\n }\n\n const localHash = forkedFrom.content_hash;\n const sourceSkill = sourceSkills[forkedFrom.skill_id];\n\n if (!sourceSkill) {\n // Skill was forked from a source that no longer exists\n results.push({\n id: forkedFrom.skill_id,\n localHash,\n sourceHash: null,\n status: \"local-only\",\n });\n continue;\n }\n\n const sourceHash = await computeSourceHash(sourcePath, sourceSkill.path);\n\n if (sourceHash === null) {\n // Source skill's SKILL.md not found\n results.push({\n id: forkedFrom.skill_id,\n localHash,\n sourceHash: null,\n status: \"local-only\",\n });\n continue;\n }\n\n const status: SkillStatus =\n localHash === sourceHash ? \"current\" : \"outdated\";\n\n results.push({\n id: forkedFrom.skill_id,\n localHash,\n sourceHash,\n status,\n });\n }\n\n // Sort results by skill ID\n results.sort((a, b) => a.id.localeCompare(b.id));\n\n return results;\n}\n\n/**\n * Calculate summary counts from comparison results\n */\nfunction calculateSummary(results: SkillComparisonResult[]): ComparisonSummary {\n return {\n outdated: results.filter((r) => r.status === \"outdated\").length,\n current: results.filter((r) => r.status === \"current\").length,\n localOnly: results.filter((r) => r.status === \"local-only\").length,\n };\n}\n\n/**\n * Format hash for display\n */\nfunction formatHash(hash: string | null, isLocal: boolean): string {\n if (hash === null) {\n return isLocal ? \"(local)\" : \"-\";\n }\n return hash;\n}\n\nexport default class Outdated extends BaseCommand {\n static summary =\n \"Check which local skills are out of date compared to source\";\n static description =\n \"Compare local skills against their source repository to identify outdated skills that need updating\";\n\n static flags = {\n ...BaseCommand.baseFlags,\n json: Flags.boolean({\n description: \"Output results as JSON\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Outdated);\n const projectDir = process.cwd();\n\n try {\n // Check if local skills directory exists\n const localSkillsPath = path.join(projectDir, LOCAL_SKILLS_PATH);\n if (!(await fileExists(localSkillsPath))) {\n if (flags.json) {\n this.log(\n JSON.stringify({\n skills: [],\n summary: { outdated: 0, current: 0, localOnly: 0 },\n }),\n );\n } else {\n this.warn(\"No local skills found. Run `cc init` or `cc edit` first.\");\n }\n return;\n }\n\n if (!flags.json) {\n this.log(\"Loading skills...\");\n }\n\n const { matrix, sourcePath, isLocal } = await loadSkillsMatrixFromSource({\n sourceFlag: flags.source,\n projectDir,\n });\n\n if (!flags.json) {\n this.log(`Loaded from ${isLocal ? \"local\" : \"remote\"}: ${sourcePath}`);\n }\n\n // Build source skills map for lookup\n const sourceSkills: Record<string, { path: string }> = {};\n for (const [skillId, skill] of Object.entries(matrix.skills)) {\n if (!skill.local) {\n sourceSkills[skillId] = { path: skill.path };\n }\n }\n\n // Compare local skills against source\n const results = await compareSkills(projectDir, sourcePath, sourceSkills);\n const summary = calculateSummary(results);\n\n // Output results\n if (flags.json) {\n this.log(\n JSON.stringify(\n {\n skills: results.map((r) => ({\n id: r.id,\n localHash: r.localHash,\n sourceHash: r.sourceHash,\n status: r.status,\n })),\n summary: {\n outdated: summary.outdated,\n current: summary.current,\n localOnly: summary.localOnly,\n },\n },\n null,\n 2,\n ),\n );\n } else {\n this.log(\"\");\n if (results.length === 0) {\n this.logInfo(\"No local skills found to compare.\");\n } else {\n // Use @oclif/table for table output\n printTable({\n data: results.map((result) => ({\n skill: result.id,\n localHash: formatHash(result.localHash, true),\n sourceHash: formatHash(result.sourceHash, false),\n status: result.status,\n })),\n columns: [\n { key: \"skill\", name: \"Skill\" },\n { key: \"localHash\", name: \"Local Hash\" },\n { key: \"sourceHash\", name: \"Source Hash\" },\n { key: \"status\", name: \"Status\" },\n ],\n headerOptions: { bold: true },\n });\n\n this.log(\"\");\n\n // Display summary\n const parts: string[] = [];\n if (summary.outdated > 0) {\n parts.push(`${summary.outdated} outdated`);\n }\n if (summary.current > 0) {\n parts.push(`${summary.current} current`);\n }\n if (summary.localOnly > 0) {\n parts.push(`${summary.localOnly} local-only`);\n }\n this.log(`Summary: ${parts.join(\", \")}`);\n }\n this.log(\"\");\n }\n\n // Exit with appropriate code if there are outdated skills\n if (summary.outdated > 0) {\n this.error(\"Some skills are outdated\", { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n if (flags.json) {\n this.error(JSON.stringify({ error: message }), {\n exit: EXIT_CODES.ERROR,\n });\n } else {\n this.error(`Error: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadSkillsMatrixFromSource
|
|
4
|
+
} from "../chunk-DKGL77IY.js";
|
|
5
|
+
import "../chunk-QGGSLMO3.js";
|
|
6
|
+
import "../chunk-NGBFJJ7Q.js";
|
|
7
|
+
import "../chunk-QESUUPOE.js";
|
|
8
|
+
import "../chunk-A3J6IAXK.js";
|
|
9
|
+
import {
|
|
10
|
+
BaseCommand
|
|
11
|
+
} from "../chunk-SYQ7R2JO.js";
|
|
12
|
+
import "../chunk-TOPAIL5W.js";
|
|
13
|
+
import "../chunk-MMDXNZPF.js";
|
|
14
|
+
import {
|
|
15
|
+
init_esm_shims
|
|
16
|
+
} from "../chunk-DHET7RCE.js";
|
|
17
|
+
|
|
18
|
+
// src/cli-v2/commands/search.ts
|
|
19
|
+
init_esm_shims();
|
|
20
|
+
import { Args, Flags } from "@oclif/core";
|
|
21
|
+
import { printTable } from "@oclif/table";
|
|
22
|
+
var MAX_DESCRIPTION_WIDTH = 50;
|
|
23
|
+
function truncate(str, maxLength) {
|
|
24
|
+
if (str.length <= maxLength) return str;
|
|
25
|
+
return str.slice(0, maxLength - 3) + "...";
|
|
26
|
+
}
|
|
27
|
+
function matchesQuery(skill, query) {
|
|
28
|
+
const lowerQuery = query.toLowerCase();
|
|
29
|
+
if (skill.name.toLowerCase().includes(lowerQuery)) return true;
|
|
30
|
+
if (skill.id.toLowerCase().includes(lowerQuery)) return true;
|
|
31
|
+
if (skill.alias?.toLowerCase().includes(lowerQuery)) return true;
|
|
32
|
+
if (skill.description.toLowerCase().includes(lowerQuery)) return true;
|
|
33
|
+
if (skill.category.toLowerCase().includes(lowerQuery)) return true;
|
|
34
|
+
if (skill.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function matchesCategory(skill, category) {
|
|
40
|
+
const lowerCategory = category.toLowerCase();
|
|
41
|
+
return skill.category.toLowerCase().includes(lowerCategory);
|
|
42
|
+
}
|
|
43
|
+
var Search = class _Search extends BaseCommand {
|
|
44
|
+
static summary = "Search available skills";
|
|
45
|
+
static description = "Search skills by name, description, category, or tags and display results in a table";
|
|
46
|
+
static args = {
|
|
47
|
+
query: Args.string({
|
|
48
|
+
description: "Search query (matches name, description, category, tags)",
|
|
49
|
+
required: true
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
static flags = {
|
|
53
|
+
...BaseCommand.baseFlags,
|
|
54
|
+
category: Flags.string({
|
|
55
|
+
char: "c",
|
|
56
|
+
description: "Filter by category",
|
|
57
|
+
required: false
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
async run() {
|
|
61
|
+
const { args, flags } = await this.parse(_Search);
|
|
62
|
+
try {
|
|
63
|
+
this.log("Loading skills...");
|
|
64
|
+
const { matrix, sourcePath, isLocal } = await loadSkillsMatrixFromSource({
|
|
65
|
+
sourceFlag: flags.source
|
|
66
|
+
});
|
|
67
|
+
this.log(`Loaded from ${isLocal ? "local" : "remote"}: ${sourcePath}`);
|
|
68
|
+
const allSkills = Object.values(matrix.skills);
|
|
69
|
+
let results = allSkills.filter(
|
|
70
|
+
(skill) => matchesQuery(skill, args.query)
|
|
71
|
+
);
|
|
72
|
+
if (flags.category) {
|
|
73
|
+
results = results.filter(
|
|
74
|
+
(skill) => matchesCategory(skill, flags.category)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
78
|
+
this.log("");
|
|
79
|
+
if (results.length === 0) {
|
|
80
|
+
this.warn(`No skills found matching "${args.query}"`);
|
|
81
|
+
if (flags.category) {
|
|
82
|
+
this.logInfo(`Category filter: ${flags.category}`);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
this.logInfo(
|
|
86
|
+
`Found ${results.length} skill${results.length === 1 ? "" : "s"} matching "${args.query}"`
|
|
87
|
+
);
|
|
88
|
+
if (flags.category) {
|
|
89
|
+
this.logInfo(`Category filter: ${flags.category}`);
|
|
90
|
+
}
|
|
91
|
+
this.log("");
|
|
92
|
+
printTable({
|
|
93
|
+
data: results.map((skill) => ({
|
|
94
|
+
id: skill.alias || skill.id,
|
|
95
|
+
category: skill.category,
|
|
96
|
+
description: truncate(skill.description, MAX_DESCRIPTION_WIDTH)
|
|
97
|
+
})),
|
|
98
|
+
columns: [
|
|
99
|
+
{ key: "id", name: "ID" },
|
|
100
|
+
{ key: "category", name: "Category" },
|
|
101
|
+
{ key: "description", name: "Description" }
|
|
102
|
+
],
|
|
103
|
+
headerOptions: { bold: true }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
this.log("");
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this.handleError(error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export {
|
|
113
|
+
Search as default
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli-v2/commands/search.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport { printTable } from \"@oclif/table\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { loadSkillsMatrixFromSource } from \"../lib/source-loader.js\";\nimport type { ResolvedSkill } from \"../types-matrix.js\";\n\n/**\n * Maximum description width for table output\n */\nconst MAX_DESCRIPTION_WIDTH = 50;\n\n/**\n * Truncate a string to a maximum length with ellipsis\n */\nfunction truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength - 3) + \"...\";\n}\n\n/**\n * Check if a skill matches the search query (case-insensitive)\n */\nfunction matchesQuery(skill: ResolvedSkill, query: string): boolean {\n const lowerQuery = query.toLowerCase();\n\n // Match against name\n if (skill.name.toLowerCase().includes(lowerQuery)) return true;\n\n // Match against ID\n if (skill.id.toLowerCase().includes(lowerQuery)) return true;\n\n // Match against alias\n if (skill.alias?.toLowerCase().includes(lowerQuery)) return true;\n\n // Match against description\n if (skill.description.toLowerCase().includes(lowerQuery)) return true;\n\n // Match against category\n if (skill.category.toLowerCase().includes(lowerQuery)) return true;\n\n // Match against tags\n if (skill.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if a skill matches the category filter (case-insensitive)\n */\nfunction matchesCategory(skill: ResolvedSkill, category: string): boolean {\n const lowerCategory = category.toLowerCase();\n return skill.category.toLowerCase().includes(lowerCategory);\n}\n\nexport default class Search extends BaseCommand {\n static summary = \"Search available skills\";\n static description =\n \"Search skills by name, description, category, or tags and display results in a table\";\n\n static args = {\n query: Args.string({\n description: \"Search query (matches name, description, category, tags)\",\n required: true,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n category: Flags.string({\n char: \"c\",\n description: \"Filter by category\",\n required: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(Search);\n\n try {\n this.log(\"Loading skills...\");\n\n const { matrix, sourcePath, isLocal } = await loadSkillsMatrixFromSource({\n sourceFlag: flags.source,\n });\n\n this.log(`Loaded from ${isLocal ? \"local\" : \"remote\"}: ${sourcePath}`);\n\n // Get all skills as array\n const allSkills = Object.values(matrix.skills);\n\n // Filter by query\n let results = allSkills.filter((skill) =>\n matchesQuery(skill, args.query),\n );\n\n // Apply category filter if provided\n if (flags.category) {\n results = results.filter((skill) =>\n matchesCategory(skill, flags.category as string),\n );\n }\n\n // Sort results by name\n results.sort((a, b) => a.name.localeCompare(b.name));\n\n // Display results\n this.log(\"\");\n if (results.length === 0) {\n this.warn(`No skills found matching \"${args.query}\"`);\n if (flags.category) {\n this.logInfo(`Category filter: ${flags.category}`);\n }\n } else {\n this.logInfo(\n `Found ${results.length} skill${results.length === 1 ? \"\" : \"s\"} matching \"${args.query}\"`,\n );\n if (flags.category) {\n this.logInfo(`Category filter: ${flags.category}`);\n }\n this.log(\"\");\n\n // Use @oclif/table for table output\n printTable({\n data: results.map((skill) => ({\n id: skill.alias || skill.id,\n category: skill.category,\n description: truncate(skill.description, MAX_DESCRIPTION_WIDTH),\n })),\n columns: [\n { key: \"id\", name: \"ID\" },\n { key: \"category\", name: \"Category\" },\n { key: \"description\", name: \"Description\" },\n ],\n headerOptions: { bold: true },\n });\n }\n this.log(\"\");\n } catch (error) {\n this.handleError(error);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAS,MAAM,aAAa;AAC5B,SAAS,kBAAkB;AAQ3B,IAAM,wBAAwB;AAK9B,SAAS,SAAS,KAAa,WAA2B;AACxD,MAAI,IAAI,UAAU,UAAW,QAAO;AACpC,SAAO,IAAI,MAAM,GAAG,YAAY,CAAC,IAAI;AACvC;AAKA,SAAS,aAAa,OAAsB,OAAwB;AAClE,QAAM,aAAa,MAAM,YAAY;AAGrC,MAAI,MAAM,KAAK,YAAY,EAAE,SAAS,UAAU,EAAG,QAAO;AAG1D,MAAI,MAAM,GAAG,YAAY,EAAE,SAAS,UAAU,EAAG,QAAO;AAGxD,MAAI,MAAM,OAAO,YAAY,EAAE,SAAS,UAAU,EAAG,QAAO;AAG5D,MAAI,MAAM,YAAY,YAAY,EAAE,SAAS,UAAU,EAAG,QAAO;AAGjE,MAAI,MAAM,SAAS,YAAY,EAAE,SAAS,UAAU,EAAG,QAAO;AAG9D,MAAI,MAAM,KAAK,KAAK,CAAC,QAAQ,IAAI,YAAY,EAAE,SAAS,UAAU,CAAC,GAAG;AACpE,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,gBAAgB,OAAsB,UAA2B;AACxE,QAAM,gBAAgB,SAAS,YAAY;AAC3C,SAAO,MAAM,SAAS,YAAY,EAAE,SAAS,aAAa;AAC5D;AAEA,IAAqB,SAArB,MAAqB,gBAAe,YAAY;AAAA,EAC9C,OAAO,UAAU;AAAA,EACjB,OAAO,cACL;AAAA,EAEF,OAAO,OAAO;AAAA,IACZ,OAAO,KAAK,OAAO;AAAA,MACjB,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,QAAQ;AAAA,IACb,GAAG,YAAY;AAAA,IACf,UAAU,MAAM,OAAO;AAAA,MACrB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,MAAM,OAAM;AAE/C,QAAI;AACF,WAAK,IAAI,mBAAmB;AAE5B,YAAM,EAAE,QAAQ,YAAY,QAAQ,IAAI,MAAM,2BAA2B;AAAA,QACvE,YAAY,MAAM;AAAA,MACpB,CAAC;AAED,WAAK,IAAI,eAAe,UAAU,UAAU,QAAQ,KAAK,UAAU,EAAE;AAGrE,YAAM,YAAY,OAAO,OAAO,OAAO,MAAM;AAG7C,UAAI,UAAU,UAAU;AAAA,QAAO,CAAC,UAC9B,aAAa,OAAO,KAAK,KAAK;AAAA,MAChC;AAGA,UAAI,MAAM,UAAU;AAClB,kBAAU,QAAQ;AAAA,UAAO,CAAC,UACxB,gBAAgB,OAAO,MAAM,QAAkB;AAAA,QACjD;AAAA,MACF;AAGA,cAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAGnD,WAAK,IAAI,EAAE;AACX,UAAI,QAAQ,WAAW,GAAG;AACxB,aAAK,KAAK,6BAA6B,KAAK,KAAK,GAAG;AACpD,YAAI,MAAM,UAAU;AAClB,eAAK,QAAQ,oBAAoB,MAAM,QAAQ,EAAE;AAAA,QACnD;AAAA,MACF,OAAO;AACL,aAAK;AAAA,UACH,SAAS,QAAQ,MAAM,SAAS,QAAQ,WAAW,IAAI,KAAK,GAAG,cAAc,KAAK,KAAK;AAAA,QACzF;AACA,YAAI,MAAM,UAAU;AAClB,eAAK,QAAQ,oBAAoB,MAAM,QAAQ,EAAE;AAAA,QACnD;AACA,aAAK,IAAI,EAAE;AAGX,mBAAW;AAAA,UACT,MAAM,QAAQ,IAAI,CAAC,WAAW;AAAA,YAC5B,IAAI,MAAM,SAAS,MAAM;AAAA,YACzB,UAAU,MAAM;AAAA,YAChB,aAAa,SAAS,MAAM,aAAa,qBAAqB;AAAA,UAChE,EAAE;AAAA,UACF,SAAS;AAAA,YACP,EAAE,KAAK,MAAM,MAAM,KAAK;AAAA,YACxB,EAAE,KAAK,YAAY,MAAM,WAAW;AAAA,YACpC,EAAE,KAAK,eAAe,MAAM,cAAc;AAAA,UAC5C;AAAA,UACA,eAAe,EAAE,MAAM,KAAK;AAAA,QAC9B,CAAC;AAAA,MACH;AACA,WAAK,IAAI,EAAE;AAAA,IACb,SAAS,OAAO;AACd,WAAK,YAAY,KAAK;AAAA,IACxB;AAAA,EACF;AACF;","names":[]}
|