d-skills 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +75 -2
- package/dist/cli.js +412 -0
- package/package.json +55 -3
- package/skills/README.md +35 -0
- package/skills/design/banner-design/SKILL.md +196 -0
- package/skills/design/banner-design/references/banner-sizes-and-styles.md +118 -0
- package/skills/design/brand/SKILL.md +97 -0
- package/skills/design/brand/references/approval-checklist.md +169 -0
- package/skills/design/brand/references/asset-organization.md +157 -0
- package/skills/design/brand/references/brand-guideline-template.md +140 -0
- package/skills/design/brand/references/color-palette-management.md +186 -0
- package/skills/design/brand/references/consistency-checklist.md +94 -0
- package/skills/design/brand/references/logo-usage-rules.md +185 -0
- package/skills/design/brand/references/messaging-framework.md +85 -0
- package/skills/design/brand/references/typography-specifications.md +214 -0
- package/skills/design/brand/references/update.md +118 -0
- package/skills/design/brand/references/visual-identity.md +96 -0
- package/skills/design/brand/references/voice-framework.md +88 -0
- package/skills/design/brand/scripts/extract-colors.cjs +341 -0
- package/skills/design/brand/scripts/inject-brand-context.cjs +349 -0
- package/skills/design/brand/scripts/sync-brand-to-tokens.cjs +248 -0
- package/skills/design/brand/scripts/tests/test_sync_brand_to_tokens.py +52 -0
- package/skills/design/brand/scripts/validate-asset.cjs +387 -0
- package/skills/design/brand/templates/brand-guidelines-starter.md +275 -0
- package/skills/design/design/SKILL.md +313 -0
- package/skills/design/design/data/cip/deliverables.csv +51 -0
- package/skills/design/design/data/cip/industries.csv +21 -0
- package/skills/design/design/data/cip/mockup-contexts.csv +21 -0
- package/skills/design/design/data/cip/styles.csv +21 -0
- package/skills/design/design/data/icon/styles.csv +16 -0
- package/skills/design/design/data/logo/colors.csv +56 -0
- package/skills/design/design/data/logo/industries.csv +56 -0
- package/skills/design/design/data/logo/styles.csv +56 -0
- package/skills/design/design/references/banner-sizes-and-styles.md +118 -0
- package/skills/design/design/references/cip-deliverable-guide.md +95 -0
- package/skills/design/design/references/cip-design.md +121 -0
- package/skills/design/design/references/cip-prompt-engineering.md +84 -0
- package/skills/design/design/references/cip-style-guide.md +68 -0
- package/skills/design/design/references/design-routing.md +207 -0
- package/skills/design/design/references/icon-design.md +122 -0
- package/skills/design/design/references/logo-color-psychology.md +101 -0
- package/skills/design/design/references/logo-design.md +92 -0
- package/skills/design/design/references/logo-prompt-engineering.md +158 -0
- package/skills/design/design/references/logo-style-guide.md +109 -0
- package/skills/design/design/references/slides-copywriting-formulas.md +84 -0
- package/skills/design/design/references/slides-create.md +4 -0
- package/skills/design/design/references/slides-html-template.md +295 -0
- package/skills/design/design/references/slides-layout-patterns.md +137 -0
- package/skills/design/design/references/slides-strategies.md +94 -0
- package/skills/design/design/references/slides.md +42 -0
- package/skills/design/design/references/social-photos-design.md +329 -0
- package/skills/design/design/scripts/cip/core.py +215 -0
- package/skills/design/design/scripts/cip/generate.py +484 -0
- package/skills/design/design/scripts/cip/render-html.py +424 -0
- package/skills/design/design/scripts/cip/search.py +127 -0
- package/skills/design/design/scripts/icon/generate.py +487 -0
- package/skills/design/design/scripts/logo/core.py +175 -0
- package/skills/design/design/scripts/logo/generate.py +362 -0
- package/skills/design/design/scripts/logo/search.py +114 -0
- package/skills/design/design-system/SKILL.md +244 -0
- package/skills/design/design-system/data/slide-backgrounds.csv +11 -0
- package/skills/design/design-system/data/slide-charts.csv +26 -0
- package/skills/design/design-system/data/slide-color-logic.csv +14 -0
- package/skills/design/design-system/data/slide-copy.csv +26 -0
- package/skills/design/design-system/data/slide-layout-logic.csv +16 -0
- package/skills/design/design-system/data/slide-layouts.csv +26 -0
- package/skills/design/design-system/data/slide-strategies.csv +16 -0
- package/skills/design/design-system/data/slide-typography.csv +15 -0
- package/skills/design/design-system/references/component-specs.md +236 -0
- package/skills/design/design-system/references/component-tokens.md +214 -0
- package/skills/design/design-system/references/primitive-tokens.md +203 -0
- package/skills/design/design-system/references/semantic-tokens.md +215 -0
- package/skills/design/design-system/references/states-and-variants.md +241 -0
- package/skills/design/design-system/references/tailwind-integration.md +251 -0
- package/skills/design/design-system/references/token-architecture.md +224 -0
- package/skills/design/design-system/scripts/embed-tokens.cjs +99 -0
- package/skills/design/design-system/scripts/fetch-background.py +317 -0
- package/skills/design/design-system/scripts/generate-slide.py +770 -0
- package/skills/design/design-system/scripts/generate-tokens.cjs +205 -0
- package/skills/design/design-system/scripts/html-token-validator.py +327 -0
- package/skills/design/design-system/scripts/search-slides.py +218 -0
- package/skills/design/design-system/scripts/slide-token-validator.py +35 -0
- package/skills/design/design-system/scripts/slide_search_core.py +453 -0
- package/skills/design/design-system/scripts/tests/test_validate_tokens.py +48 -0
- package/skills/design/design-system/scripts/validate-tokens.cjs +246 -0
- package/skills/design/design-system/templates/design-tokens-starter.json +143 -0
- package/skills/design/frontend-design/SKILL.md +74 -0
- package/skills/design/slides/SKILL.md +40 -0
- package/skills/design/slides/references/copywriting-formulas.md +84 -0
- package/skills/design/slides/references/create.md +4 -0
- package/skills/design/slides/references/html-template.md +295 -0
- package/skills/design/slides/references/layout-patterns.md +137 -0
- package/skills/design/slides/references/slide-strategies.md +94 -0
- package/skills/design/ui-styling/LICENSE.txt +202 -0
- package/skills/design/ui-styling/SKILL.md +324 -0
- package/skills/design/ui-styling/references/canvas-design-system.md +320 -0
- package/skills/design/ui-styling/references/shadcn-accessibility.md +471 -0
- package/skills/design/ui-styling/references/shadcn-components.md +424 -0
- package/skills/design/ui-styling/references/shadcn-theming.md +373 -0
- package/skills/design/ui-styling/references/tailwind-customization.md +483 -0
- package/skills/design/ui-styling/references/tailwind-responsive.md +382 -0
- package/skills/design/ui-styling/references/tailwind-utilities.md +463 -0
- package/skills/design/ui-styling/scripts/requirements.txt +17 -0
- package/skills/design/ui-styling/scripts/shadcn_add.py +308 -0
- package/skills/design/ui-styling/scripts/tailwind_config_gen.py +473 -0
- package/skills/design/ui-styling/scripts/tests/coverage-ui.json +1 -0
- package/skills/design/ui-styling/scripts/tests/requirements.txt +3 -0
- package/skills/design/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
- package/skills/design/ui-styling/scripts/tests/test_tailwind_config_gen.py +394 -0
- package/skills/design/ui-ux-pro-max/SKILL.md +425 -0
- package/skills/design/ui-ux-pro-max/data/app-interface.csv +33 -0
- package/skills/design/ui-ux-pro-max/data/catalog-summary.json +78 -0
- package/skills/design/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/design/ui-ux-pro-max/data/colors.csv +193 -0
- package/skills/design/ui-ux-pro-max/data/data-provenance.json +1348 -0
- package/skills/design/ui-ux-pro-max/data/google-font-licenses.json +20185 -0
- package/skills/design/ui-ux-pro-max/data/google-fonts.csv +1935 -0
- package/skills/design/ui-ux-pro-max/data/icons.csv +106 -0
- package/skills/design/ui-ux-pro-max/data/landing.csv +35 -0
- package/skills/design/ui-ux-pro-max/data/motion.csv +18 -0
- package/skills/design/ui-ux-pro-max/data/phosphor-icons-upstream.json +32582 -0
- package/skills/design/ui-ux-pro-max/data/products.csv +193 -0
- package/skills/design/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/design/ui-ux-pro-max/data/stacks/angular.csv +51 -0
- package/skills/design/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/design/ui-ux-pro-max/data/stacks/avalonia.csv +57 -0
- package/skills/design/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/design/ui-ux-pro-max/data/stacks/html-tailwind.csv +60 -0
- package/skills/design/ui-ux-pro-max/data/stacks/javafx.csv +76 -0
- package/skills/design/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/design/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
- package/skills/design/ui-ux-pro-max/data/stacks/nextjs.csv +62 -0
- package/skills/design/ui-ux-pro-max/data/stacks/nuxt-ui.csv +71 -0
- package/skills/design/ui-ux-pro-max/data/stacks/nuxtjs.csv +68 -0
- package/skills/design/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/design/ui-ux-pro-max/data/stacks/react.csv +62 -0
- package/skills/design/ui-ux-pro-max/data/stacks/shadcn.csv +69 -0
- package/skills/design/ui-ux-pro-max/data/stacks/svelte.csv +56 -0
- package/skills/design/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/design/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
- package/skills/design/ui-ux-pro-max/data/stacks/uno.csv +60 -0
- package/skills/design/ui-ux-pro-max/data/stacks/uwp.csv +56 -0
- package/skills/design/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/design/ui-ux-pro-max/data/stacks/winui.csv +60 -0
- package/skills/design/ui-ux-pro-max/data/stacks/wpf.csv +57 -0
- package/skills/design/ui-ux-pro-max/data/styles.csv +89 -0
- package/skills/design/ui-ux-pro-max/data/typography.csv +75 -0
- package/skills/design/ui-ux-pro-max/data/ui-reasoning.csv +193 -0
- package/skills/design/ui-ux-pro-max/data/ux-guidelines.csv +120 -0
- package/skills/design/ui-ux-pro-max/scripts/core.py +993 -0
- package/skills/design/ui-ux-pro-max/scripts/design_system.py +1643 -0
- package/skills/design/ui-ux-pro-max/scripts/reasoning_contract.py +123 -0
- package/skills/design/ui-ux-pro-max/scripts/search.py +171 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-api.json +36 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-catalog.json +33 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-existing.csv +3 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-metadata.json +17 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-overrides.json +7 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/icons-curated.csv +4 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-core.json +23 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-package.json +13 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-exports.json +4 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-package.json +5 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/relevance-baseline.json +3366 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/relevance-cases.json +111 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/fixtures/relevance-thresholds.json +100 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_catalog_refresh.py +354 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_core.py +343 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_core_data_quality.py +198 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_data_contracts.py +421 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +189 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_native_desktop_stack_freshness.py +176 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_relevance_evaluator.py +182 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_style_taxonomy.py +175 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_text_layout_resilience.py +120 -0
- package/skills/design/ui-ux-pro-max/scripts/tests/test_web_stack_freshness.py +177 -0
- package/skills/design/ui-ux-pro-max/scripts/validate_data.py +1091 -0
- package/skills/integrations/tanstack-google-analytics/SKILL.md +309 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Davi Bezerra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# d-skills
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Install agent skills into any project.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx d-skills install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Run without flags for the interactive flow: pick an install directory, then
|
|
10
|
+
pick categories. Skills land directly under the target directory
|
|
11
|
+
(`<dir>/<skill>`), never nested by category.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npx d-skills install --dir .agents/skills --all
|
|
17
|
+
npx d-skills install --category design
|
|
18
|
+
npx d-skills install --category design --category integrations
|
|
19
|
+
npx d-skills install --category design --offline
|
|
20
|
+
npx d-skills install --dir /tmp/demo --dry-run
|
|
21
|
+
npx d-skills list
|
|
22
|
+
npx d-skills list --categories
|
|
23
|
+
npx d-skills list --json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Options
|
|
27
|
+
|
|
28
|
+
| Flag | Default | Purpose |
|
|
29
|
+
| -------------------- | ----------------- | ------------------------------------------------ |
|
|
30
|
+
| `-d, --dir <path>` | `.agents/skills` | Install directory (`~` and relative paths work) |
|
|
31
|
+
| `-c, --category <n>` | all | Install one category (repeatable) |
|
|
32
|
+
| `-a, --all` | | Install all categories |
|
|
33
|
+
| `-f, --force` | | Overwrite skills that already exist |
|
|
34
|
+
| `--offline` | | Use bundled `skills/` instead of downloading |
|
|
35
|
+
| `--dry-run` | | Print the plan without copying |
|
|
36
|
+
|
|
37
|
+
Existing skills are skipped unless `--force` is given. Unknown categories
|
|
38
|
+
fail fast with a suggestion, e.g. `Did you mean: "desgin" → "design"?`.
|
|
39
|
+
Piped runs never prompt: they default to all categories and `--dir`.
|
|
40
|
+
|
|
41
|
+
## Install targets
|
|
42
|
+
|
|
43
|
+
The interactive directory prompt offers presets plus a custom path:
|
|
44
|
+
|
|
45
|
+
- `.agents/skills` (default)
|
|
46
|
+
- `.claude/skills`
|
|
47
|
+
- `.codex/skills`
|
|
48
|
+
- `.cursor/skills`
|
|
49
|
+
- Custom path
|
|
50
|
+
|
|
51
|
+
## Source
|
|
52
|
+
|
|
53
|
+
By default skills are downloaded from GitHub, so installs always get the
|
|
54
|
+
latest version without republishing the npm package. Tag and SHA refs are
|
|
55
|
+
cached under the OS temp directory; branch refs are re-downloaded.
|
|
56
|
+
|
|
57
|
+
| Variable | Default | Purpose |
|
|
58
|
+
| ------------------ | ------------------------------- | ------------------------ |
|
|
59
|
+
| `DSKILLS_REPO` | `DaviRJ/d-skills` | Source repository |
|
|
60
|
+
| `DSKILLS_REF` | `main` | Branch, tag, or commit |
|
|
61
|
+
| `DSKILLS_BASE_URL` | `https://codeload.github.com` | Tarball host (advanced) |
|
|
62
|
+
|
|
63
|
+
## Skills
|
|
64
|
+
|
|
65
|
+
Skills live in `skills/<category>/<skill>/`. See `skills/README.md` for how
|
|
66
|
+
to add a skill or category.
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
pnpm install
|
|
72
|
+
pnpm validate:skills
|
|
73
|
+
pnpm build
|
|
74
|
+
pnpm test
|
|
75
|
+
node dist/cli.js --help
|
|
76
|
+
```
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
6
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
// src/commands/install.ts
|
|
10
|
+
import { mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
11
|
+
import { homedir, tmpdir as tmpdir2 } from "os";
|
|
12
|
+
import { isAbsolute, join as join4, resolve } from "path";
|
|
13
|
+
import * as p2 from "@clack/prompts";
|
|
14
|
+
|
|
15
|
+
// src/lib/categories.ts
|
|
16
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
function isDirectory(path) {
|
|
19
|
+
try {
|
|
20
|
+
return statSync(path).isDirectory();
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function parseFrontmatterField(content, field) {
|
|
26
|
+
const block = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
27
|
+
if (!block) return null;
|
|
28
|
+
const line = block[1].match(new RegExp(`^${field}:\\s*(.+?)\\s*$`, "m"));
|
|
29
|
+
if (!line) return null;
|
|
30
|
+
return line[1].replace(/^["']|["']$/g, "").trim();
|
|
31
|
+
}
|
|
32
|
+
function discoverCategories(skillsRoot2) {
|
|
33
|
+
if (!isDirectory(skillsRoot2)) return [];
|
|
34
|
+
const categories = [];
|
|
35
|
+
for (const categoryName of readdirSync(skillsRoot2).sort()) {
|
|
36
|
+
const categoryDir = join(skillsRoot2, categoryName);
|
|
37
|
+
if (!isDirectory(categoryDir)) continue;
|
|
38
|
+
const skills = [];
|
|
39
|
+
for (const skillName of readdirSync(categoryDir).sort()) {
|
|
40
|
+
const skillDir = join(categoryDir, skillName);
|
|
41
|
+
if (!isDirectory(skillDir)) continue;
|
|
42
|
+
let description = "";
|
|
43
|
+
try {
|
|
44
|
+
const skillFile = readFileSync(join(skillDir, "SKILL.md"), "utf8");
|
|
45
|
+
description = parseFrontmatterField(skillFile, "description") ?? "";
|
|
46
|
+
} catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
skills.push({
|
|
50
|
+
name: skillName,
|
|
51
|
+
description,
|
|
52
|
+
category: categoryName,
|
|
53
|
+
dir: skillDir
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
categories.push({ name: categoryName, dir: categoryDir, skills });
|
|
57
|
+
}
|
|
58
|
+
return categories;
|
|
59
|
+
}
|
|
60
|
+
function levenshtein(a, b) {
|
|
61
|
+
const grid = Array.from({ length: a.length + 1 }, (_, i) => [
|
|
62
|
+
i,
|
|
63
|
+
...Array(b.length).fill(0)
|
|
64
|
+
]);
|
|
65
|
+
for (let j = 1; j <= b.length; j++) grid[0][j] = j;
|
|
66
|
+
for (let i = 1; i <= a.length; i++) {
|
|
67
|
+
for (let j = 1; j <= b.length; j++) {
|
|
68
|
+
grid[i][j] = Math.min(
|
|
69
|
+
grid[i - 1][j] + 1,
|
|
70
|
+
grid[i][j - 1] + 1,
|
|
71
|
+
grid[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return grid[a.length][b.length];
|
|
76
|
+
}
|
|
77
|
+
function suggestSimilar(input, names) {
|
|
78
|
+
const lower = input.toLowerCase();
|
|
79
|
+
const partial = names.find(
|
|
80
|
+
(name) => name.toLowerCase().includes(lower) || lower.includes(name.toLowerCase())
|
|
81
|
+
);
|
|
82
|
+
if (partial) return partial;
|
|
83
|
+
let best;
|
|
84
|
+
let bestDist = 3;
|
|
85
|
+
for (const name of names) {
|
|
86
|
+
const dist = levenshtein(lower, name.toLowerCase());
|
|
87
|
+
if (dist < bestDist) {
|
|
88
|
+
bestDist = dist;
|
|
89
|
+
best = name;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return best;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/lib/github.ts
|
|
96
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2 } from "fs";
|
|
97
|
+
import { tmpdir } from "os";
|
|
98
|
+
import { join as join2 } from "path";
|
|
99
|
+
import { writeFile } from "fs/promises";
|
|
100
|
+
import * as tar from "tar";
|
|
101
|
+
var DEFAULT_REPO = "DaviRJ/d-skills";
|
|
102
|
+
var DEFAULT_REF = "main";
|
|
103
|
+
function resolveRepo() {
|
|
104
|
+
return process.env.DSKILLS_REPO ?? DEFAULT_REPO;
|
|
105
|
+
}
|
|
106
|
+
function resolveRef() {
|
|
107
|
+
return process.env.DSKILLS_REF ?? DEFAULT_REF;
|
|
108
|
+
}
|
|
109
|
+
function tarballUrl(repo, ref) {
|
|
110
|
+
const base = process.env.DSKILLS_BASE_URL ?? "https://codeload.github.com";
|
|
111
|
+
return `${base}/${repo}/tar.gz/${ref}`;
|
|
112
|
+
}
|
|
113
|
+
function isCacheableRef(ref) {
|
|
114
|
+
return /^[0-9a-f]{7,40}$/i.test(ref) || /^v?\d+\.\d+\.\d+/.test(ref);
|
|
115
|
+
}
|
|
116
|
+
function cachePath(repo, ref) {
|
|
117
|
+
return join2(
|
|
118
|
+
tmpdir(),
|
|
119
|
+
"dskills-cache",
|
|
120
|
+
repo.replace("/", "-"),
|
|
121
|
+
`${ref}.tar.gz`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
async function downloadTarball(repo, ref) {
|
|
125
|
+
const dest = cachePath(repo, ref);
|
|
126
|
+
if (isCacheableRef(ref) && existsSync(dest)) return dest;
|
|
127
|
+
let response;
|
|
128
|
+
try {
|
|
129
|
+
response = await fetch(tarballUrl(repo, ref));
|
|
130
|
+
} catch (cause) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Cannot download skills from GitHub (${repo}@${ref}): ${cause.message}. Check your network or retry with --offline.`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (!response.ok || !response.body) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Cannot download skills from GitHub (${repo}@${ref}): HTTP ${response.status}. Check the repo and ref, or retry with --offline.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
mkdirSync(join2(dest, ".."), { recursive: true });
|
|
141
|
+
await writeFile(dest, Buffer.from(await response.arrayBuffer()));
|
|
142
|
+
return dest;
|
|
143
|
+
}
|
|
144
|
+
async function listTarballCategories(tarballPath) {
|
|
145
|
+
const categories = /* @__PURE__ */ new Set();
|
|
146
|
+
try {
|
|
147
|
+
const head = readFileSync2(tarballPath).subarray(0, 2);
|
|
148
|
+
if (head.length < 2 || head[0] !== 31 || head[1] !== 139) {
|
|
149
|
+
throw new Error("not a gzip archive");
|
|
150
|
+
}
|
|
151
|
+
await tar.t({
|
|
152
|
+
file: tarballPath,
|
|
153
|
+
onentry: (entry) => {
|
|
154
|
+
const parts = entry.path.split("/").filter((p3) => p3 !== "");
|
|
155
|
+
if (parts.length >= 3 && parts[1] === "skills")
|
|
156
|
+
categories.add(parts[2]);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
} catch (cause) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Cannot read downloaded archive: ${cause.message}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return [...categories].sort();
|
|
165
|
+
}
|
|
166
|
+
async function extractSkills(tarballPath, destRoot, selection) {
|
|
167
|
+
try {
|
|
168
|
+
await tar.x({
|
|
169
|
+
file: tarballPath,
|
|
170
|
+
cwd: destRoot,
|
|
171
|
+
strip: 1,
|
|
172
|
+
filter: (path) => {
|
|
173
|
+
const parts = path.split("/").filter((p3) => p3 !== "");
|
|
174
|
+
if (parts.length < 2 || parts[1] !== "skills") return false;
|
|
175
|
+
if (parts.length === 2) return true;
|
|
176
|
+
return selection.length === 0 || selection.includes(parts[2]);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
} catch (cause) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Cannot extract downloaded archive: ${cause.message}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/lib/installer.ts
|
|
187
|
+
import { cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, rmSync } from "fs";
|
|
188
|
+
import { dirname, join as join3 } from "path";
|
|
189
|
+
function planInstall(categories, selection, targetDir) {
|
|
190
|
+
const wanted = selection.length === 0 ? categories : categories.filter((c) => selection.includes(c.name));
|
|
191
|
+
const plan = [];
|
|
192
|
+
for (const category of wanted) {
|
|
193
|
+
for (const skill of category.skills) {
|
|
194
|
+
plan.push({
|
|
195
|
+
skill: skill.name,
|
|
196
|
+
category: category.name,
|
|
197
|
+
sourceDir: skill.dir,
|
|
198
|
+
targetDir: join3(targetDir, skill.name)
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return plan;
|
|
203
|
+
}
|
|
204
|
+
async function runInstall(plan, options = {}) {
|
|
205
|
+
const { force = false, dryRun = false } = options;
|
|
206
|
+
const results = [];
|
|
207
|
+
for (const item of plan) {
|
|
208
|
+
const exists = existsSync2(item.targetDir);
|
|
209
|
+
if (exists && !force) {
|
|
210
|
+
results.push({ ...item, status: "skipped" });
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (!dryRun) {
|
|
214
|
+
mkdirSync2(dirname(item.targetDir), { recursive: true });
|
|
215
|
+
if (exists) rmSync(item.targetDir, { recursive: true, force: true });
|
|
216
|
+
cpSync(item.sourceDir, item.targetDir, { recursive: true });
|
|
217
|
+
}
|
|
218
|
+
results.push({ ...item, status: exists ? "overwritten" : "installed" });
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/lib/prompts.ts
|
|
224
|
+
import * as p from "@clack/prompts";
|
|
225
|
+
var CUSTOM_DIR = "__custom__";
|
|
226
|
+
var INSTALL_PRESETS = [
|
|
227
|
+
".agents/skills",
|
|
228
|
+
".claude/skills",
|
|
229
|
+
".codex/skills",
|
|
230
|
+
".cursor/skills"
|
|
231
|
+
];
|
|
232
|
+
async function askInstallDir(defaultDir) {
|
|
233
|
+
const presets = [.../* @__PURE__ */ new Set([defaultDir, ...INSTALL_PRESETS])];
|
|
234
|
+
const choice = await p.select({
|
|
235
|
+
message: "Install directory?",
|
|
236
|
+
options: [
|
|
237
|
+
...presets.map((dir) => ({ value: dir, label: dir })),
|
|
238
|
+
{ value: CUSTOM_DIR, label: "Custom path..." }
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
if (p.isCancel(choice)) {
|
|
242
|
+
p.cancel("Cancelled.");
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
if (choice !== CUSTOM_DIR) return choice;
|
|
246
|
+
const custom = await p.text({
|
|
247
|
+
message: "Custom directory?",
|
|
248
|
+
placeholder: defaultDir
|
|
249
|
+
});
|
|
250
|
+
if (p.isCancel(custom)) {
|
|
251
|
+
p.cancel("Cancelled.");
|
|
252
|
+
process.exit(0);
|
|
253
|
+
}
|
|
254
|
+
const trimmed = String(custom).trim();
|
|
255
|
+
return trimmed === "" ? defaultDir : trimmed;
|
|
256
|
+
}
|
|
257
|
+
async function askScope(categories) {
|
|
258
|
+
const value = await p.multiselect({
|
|
259
|
+
message: "Which categories to install?",
|
|
260
|
+
options: categories.map((c) => ({
|
|
261
|
+
value: c.name,
|
|
262
|
+
label: c.name,
|
|
263
|
+
hint: `${c.skills.length} skills`
|
|
264
|
+
})),
|
|
265
|
+
initialValues: categories.map((c) => c.name),
|
|
266
|
+
required: true
|
|
267
|
+
});
|
|
268
|
+
if (p.isCancel(value)) {
|
|
269
|
+
p.cancel("Cancelled.");
|
|
270
|
+
process.exit(0);
|
|
271
|
+
}
|
|
272
|
+
const selected = value;
|
|
273
|
+
if (selected.length === categories.length) return [];
|
|
274
|
+
return selected;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/commands/install.ts
|
|
278
|
+
var DEFAULT_DIR = ".agents/skills";
|
|
279
|
+
function resolveTargetDir(input) {
|
|
280
|
+
if (input === "~") return homedir();
|
|
281
|
+
const expanded = input.startsWith("~/") ? join4(homedir(), input.slice(2)) : input;
|
|
282
|
+
return isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);
|
|
283
|
+
}
|
|
284
|
+
function isInteractive() {
|
|
285
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
286
|
+
}
|
|
287
|
+
function assertKnownCategories(requested, names) {
|
|
288
|
+
const unknown = requested.filter((c) => !names.includes(c));
|
|
289
|
+
if (unknown.length === 0) return;
|
|
290
|
+
const hints = unknown.flatMap((u) => {
|
|
291
|
+
const match = suggestSimilar(u, names);
|
|
292
|
+
return match ? [`"${u}" \u2192 "${match}"`] : [];
|
|
293
|
+
});
|
|
294
|
+
throw new Error(
|
|
295
|
+
`Unknown categor${unknown.length === 1 ? "y" : "ies"}: ${unknown.join(", ")}.${hints.length > 0 ? ` Did you mean: ${hints.join(", ")}?` : ""} Available: ${names.join(", ")}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
async function runInstallCommand(options) {
|
|
299
|
+
const requested = options.all ? [] : options.category ?? [];
|
|
300
|
+
let categories;
|
|
301
|
+
let cleanup;
|
|
302
|
+
if (options.offline) {
|
|
303
|
+
categories = discoverCategories(options.skillsRoot);
|
|
304
|
+
if (categories.length === 0) {
|
|
305
|
+
throw new Error(`No skills found in ${options.skillsRoot}.`);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
const repo = resolveRepo();
|
|
309
|
+
const ref = resolveRef();
|
|
310
|
+
const tarball = await downloadTarball(repo, ref);
|
|
311
|
+
const available = await listTarballCategories(tarball);
|
|
312
|
+
if (available.length === 0) {
|
|
313
|
+
throw new Error(`No skills found in ${repo}@${ref}.`);
|
|
314
|
+
}
|
|
315
|
+
assertKnownCategories(requested, available);
|
|
316
|
+
const workDir = mkdtempSync(join4(tmpdir2(), "dskills-"));
|
|
317
|
+
cleanup = () => rmSync2(workDir, { recursive: true, force: true });
|
|
318
|
+
await extractSkills(tarball, workDir, requested);
|
|
319
|
+
categories = discoverCategories(join4(workDir, "skills"));
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
assertKnownCategories(
|
|
323
|
+
requested,
|
|
324
|
+
categories.map((c) => c.name)
|
|
325
|
+
);
|
|
326
|
+
const interactive = isInteractive();
|
|
327
|
+
let dir = options.dir;
|
|
328
|
+
let selection = requested;
|
|
329
|
+
if (interactive) {
|
|
330
|
+
p2.intro("d-skills");
|
|
331
|
+
if (dir === void 0) dir = await askInstallDir(DEFAULT_DIR);
|
|
332
|
+
if (!options.all && selection.length === 0)
|
|
333
|
+
selection = await askScope(categories);
|
|
334
|
+
}
|
|
335
|
+
const targetDir = resolveTargetDir(dir ?? DEFAULT_DIR);
|
|
336
|
+
const plan = planInstall(categories, selection, targetDir);
|
|
337
|
+
if (options.dryRun) {
|
|
338
|
+
console.log(`Would install ${plan.length} skills to ${targetDir}:`);
|
|
339
|
+
for (const item of plan)
|
|
340
|
+
console.log(` ${item.skill} (${item.category})`);
|
|
341
|
+
if (interactive) p2.outro("Dry run \u2014 nothing was copied.");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const results = await runInstall(plan, { force: options.force });
|
|
345
|
+
const counts = { installed: 0, overwritten: 0, skipped: 0 };
|
|
346
|
+
for (const result of results) {
|
|
347
|
+
counts[result.status] += 1;
|
|
348
|
+
console.log(`${result.status} ${result.skill} (${result.category})`);
|
|
349
|
+
}
|
|
350
|
+
const summary = `Done: ${counts.installed} installed, ${counts.overwritten} overwritten, ${counts.skipped} skipped \u2192 ${targetDir}`;
|
|
351
|
+
if (interactive) {
|
|
352
|
+
p2.outro(`${summary}
|
|
353
|
+
Skills land directly under the target directory.`);
|
|
354
|
+
} else {
|
|
355
|
+
console.log(summary);
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
cleanup?.();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/commands/list.ts
|
|
363
|
+
function runList(options) {
|
|
364
|
+
const categories = discoverCategories(options.skillsRoot);
|
|
365
|
+
if (options.json) {
|
|
366
|
+
console.log(JSON.stringify(categories, null, 2));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (categories.length === 0) {
|
|
370
|
+
console.log("No skills found.");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
for (const category of categories) {
|
|
374
|
+
console.log(`${category.name} (${category.skills.length})`);
|
|
375
|
+
if (!options.categories) {
|
|
376
|
+
for (const skill of category.skills) {
|
|
377
|
+
console.log(` - ${skill.name}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/cli.ts
|
|
384
|
+
var rootDir = join5(dirname2(fileURLToPath(import.meta.url)), "..");
|
|
385
|
+
var skillsRoot = join5(rootDir, "skills");
|
|
386
|
+
function packageVersion() {
|
|
387
|
+
try {
|
|
388
|
+
const pkg = JSON.parse(readFileSync3(join5(rootDir, "package.json"), "utf8"));
|
|
389
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
390
|
+
} catch {
|
|
391
|
+
return "0.0.0";
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
var program = new Command();
|
|
395
|
+
program.name("d-skills").description("Install agent skills into any project").version(packageVersion());
|
|
396
|
+
program.command("install").description("Install skills into a local directory").option("-d, --dir <path>", "install directory").option(
|
|
397
|
+
"-c, --category <name>",
|
|
398
|
+
"install single category (repeatable)",
|
|
399
|
+
(value, acc) => [...acc, value],
|
|
400
|
+
[]
|
|
401
|
+
).option("-a, --all", "install all categories").option("-f, --force", "overwrite existing skills").option("--offline", "use bundled skills instead of GitHub").option("--dry-run", "print plan without copying").action(async (opts) => {
|
|
402
|
+
try {
|
|
403
|
+
await runInstallCommand({ ...opts, skillsRoot });
|
|
404
|
+
} catch (error) {
|
|
405
|
+
console.error(`error: ${error.message}`);
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
program.command("list").description("List available categories and skills").option("--json", "machine-readable output").option("--categories", "category names only").action((opts) => {
|
|
410
|
+
runList({ ...opts, skillsRoot });
|
|
411
|
+
});
|
|
412
|
+
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d-skills",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Install agent skills into any project",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Davi Bezerra",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/DaviRJ/d-skills.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/DaviRJ/d-skills#readme",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"d-skills": "./dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"skills",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"skills",
|
|
26
|
+
"agents",
|
|
27
|
+
"cli"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"devEngines": {
|
|
33
|
+
"packageManager": {
|
|
34
|
+
"name": "pnpm",
|
|
35
|
+
"version": "11.28.5",
|
|
36
|
+
"onFail": "download"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@clack/prompts": "^1.8.1",
|
|
41
|
+
"commander": "^15.0.0",
|
|
42
|
+
"tar": "^7.5.22"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^26.6.4",
|
|
46
|
+
"@types/tar": "^7.0.87",
|
|
47
|
+
"tsup": "^8.5.1",
|
|
48
|
+
"typescript": "^7.0.2",
|
|
49
|
+
"vitest": "^5.0.3"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"lint": "tsc --noEmit",
|
|
56
|
+
"validate:skills": "node scripts/validate-skills.mjs"
|
|
57
|
+
}
|
|
6
58
|
}
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Skills
|
|
2
|
+
|
|
3
|
+
Skills are grouped by category using plain folders:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
skills/<category>/<skill>/SKILL.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Current categories:
|
|
10
|
+
|
|
11
|
+
- `design` — visual and content design skills
|
|
12
|
+
- `integrations` — third-party service integrations
|
|
13
|
+
|
|
14
|
+
## Add a new skill
|
|
15
|
+
|
|
16
|
+
1. Create `skills/<category>/<skill>/`.
|
|
17
|
+
2. Add `SKILL.md` with frontmatter where `name` matches the folder name:
|
|
18
|
+
|
|
19
|
+
```md
|
|
20
|
+
---
|
|
21
|
+
name: my-skill
|
|
22
|
+
description: What this skill does.
|
|
23
|
+
---
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. Add optional `references/`, `scripts/`, `templates/`, `data/` next to `SKILL.md`.
|
|
27
|
+
4. Run `pnpm validate:skills`.
|
|
28
|
+
|
|
29
|
+
## Add a new category
|
|
30
|
+
|
|
31
|
+
1. Create `skills/<new-category>/` using lowercase letters, numbers, and dashes.
|
|
32
|
+
2. Add one or more skills inside it.
|
|
33
|
+
3. Run `pnpm validate:skills`.
|
|
34
|
+
|
|
35
|
+
No registry file or code change is required. The CLI discovers categories by scanning directories.
|