@skilly-hand/skilly-hand 0.1.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 +17 -0
- package/LICENSE +23 -0
- package/README.md +107 -0
- package/catalog/README.md +30 -0
- package/catalog/catalog-index.json +6 -0
- package/catalog/skills/agents-root-orchestrator/SKILL.md +135 -0
- package/catalog/skills/agents-root-orchestrator/assets/AGENTS-ROOT-TEMPLATE.md +67 -0
- package/catalog/skills/agents-root-orchestrator/manifest.json +34 -0
- package/catalog/skills/skill-creator/SKILL.md +176 -0
- package/catalog/skills/skill-creator/assets/SKILL-TEMPLATE.md +94 -0
- package/catalog/skills/skill-creator/manifest.json +36 -0
- package/catalog/skills/spec-driven-development/SKILL.md +168 -0
- package/catalog/skills/spec-driven-development/agents/apply.md +26 -0
- package/catalog/skills/spec-driven-development/agents/orchestrate.md +25 -0
- package/catalog/skills/spec-driven-development/agents/plan.md +27 -0
- package/catalog/skills/spec-driven-development/agents/verify.md +24 -0
- package/catalog/skills/spec-driven-development/assets/delta-spec-template.md +51 -0
- package/catalog/skills/spec-driven-development/assets/design-template.md +31 -0
- package/catalog/skills/spec-driven-development/assets/spec-template.md +56 -0
- package/catalog/skills/spec-driven-development/assets/validation-checklist.md +32 -0
- package/catalog/skills/spec-driven-development/manifest.json +41 -0
- package/catalog/skills/token-optimizer/SKILL.md +141 -0
- package/catalog/skills/token-optimizer/manifest.json +33 -0
- package/catalog/skills/token-optimizer/references/complexity-indicators.md +138 -0
- package/catalog/templates/AGENTS.template.md +37 -0
- package/package.json +41 -0
- package/packages/catalog/package.json +6 -0
- package/packages/catalog/src/index.js +223 -0
- package/packages/cli/package.json +9 -0
- package/packages/cli/src/bin.js +144 -0
- package/packages/core/package.json +6 -0
- package/packages/core/src/index.js +304 -0
- package/packages/detectors/package.json +6 -0
- package/packages/detectors/src/index.js +169 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Task Complexity Indicators
|
|
2
|
+
|
|
3
|
+
Use this reference to score task complexity before selecting reasoning depth and token budget.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scoring Bands
|
|
8
|
+
|
|
9
|
+
| Total Points | Tier |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| 0-2 | Trivial |
|
|
12
|
+
| 3-5 | Simple |
|
|
13
|
+
| 6-10 | Moderate |
|
|
14
|
+
| 11-15 | Complex |
|
|
15
|
+
| 16+ | Expert |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Scoring Matrix
|
|
20
|
+
|
|
21
|
+
### File Scope
|
|
22
|
+
|
|
23
|
+
| Indicator | Points |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Single file, known location | 0 |
|
|
26
|
+
| Single file, needs search | +1 |
|
|
27
|
+
| 2-3 related files | +2 |
|
|
28
|
+
| 4-6 files | +4 |
|
|
29
|
+
| 7+ files or cross-package | +6 |
|
|
30
|
+
| Build/config/tooling impact | +3 |
|
|
31
|
+
|
|
32
|
+
### Decision Complexity
|
|
33
|
+
|
|
34
|
+
| Indicator | Points |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| Single obvious approach | 0 |
|
|
37
|
+
| 2-3 valid approaches, clear best option | +2 |
|
|
38
|
+
| Multiple trade-off-heavy approaches | +4 |
|
|
39
|
+
| Architectural design required | +6 |
|
|
40
|
+
| System-wide pattern impact | +8 |
|
|
41
|
+
|
|
42
|
+
### Context Requirements
|
|
43
|
+
|
|
44
|
+
| Indicator | Points |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| Can answer from current context | 0 |
|
|
47
|
+
| Requires 1-2 file reads | +1 |
|
|
48
|
+
| Requires 3-5 file reads | +2 |
|
|
49
|
+
| Requires codebase-wide search | +3 |
|
|
50
|
+
| Requires dependency understanding | +4 |
|
|
51
|
+
| Requires external docs/specs | +2 |
|
|
52
|
+
|
|
53
|
+
### Impact Level
|
|
54
|
+
|
|
55
|
+
| Indicator | Points |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| Internal helper only | 0 |
|
|
58
|
+
| Private module behavior | +1 |
|
|
59
|
+
| Public API behavior | +3 |
|
|
60
|
+
| Shared utility used broadly | +4 |
|
|
61
|
+
| Breaking public change | +6 |
|
|
62
|
+
| Security-sensitive code | +8 |
|
|
63
|
+
| Performance-critical path | +6 |
|
|
64
|
+
|
|
65
|
+
### Testing and Validation
|
|
66
|
+
|
|
67
|
+
| Indicator | Points |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| No tests needed | 0 |
|
|
70
|
+
| Small unit test update | +1 |
|
|
71
|
+
| Multiple test scenarios | +2 |
|
|
72
|
+
| Integration/e2e coverage changes | +3 |
|
|
73
|
+
| Manual QA verification required | +3 |
|
|
74
|
+
| Accessibility validation required | +4 |
|
|
75
|
+
|
|
76
|
+
### User Clarity (Reduction)
|
|
77
|
+
|
|
78
|
+
| Indicator | Points |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| Vague request | 0 |
|
|
81
|
+
| Clear goal | -1 |
|
|
82
|
+
| Exact file paths provided | -2 |
|
|
83
|
+
| Specific line numbers provided | -3 |
|
|
84
|
+
| Reference implementation provided | -4 |
|
|
85
|
+
| Explicit acceptance criteria | -2 |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Keyword Signals
|
|
90
|
+
|
|
91
|
+
High-complexity signals (+3 to +5):
|
|
92
|
+
|
|
93
|
+
- optimize, performance, security, refactor, migrate, architect, scale
|
|
94
|
+
|
|
95
|
+
Moderate signals (+1 to +2):
|
|
96
|
+
|
|
97
|
+
- improve, extend, integrate, validate, fix bug
|
|
98
|
+
|
|
99
|
+
Low signals (0):
|
|
100
|
+
|
|
101
|
+
- show, list, find, read, check existence
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Context Size Heuristic
|
|
106
|
+
|
|
107
|
+
| Context Size | Reads/Searches | Typical Tier |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| Minimal | 0-1 | Trivial-Simple |
|
|
110
|
+
| Light | 2-3 | Simple-Moderate |
|
|
111
|
+
| Moderate | 4-7 | Moderate |
|
|
112
|
+
| Heavy | 8-15 | Complex |
|
|
113
|
+
| Extensive | 16+ | Expert |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Reassessment Triggers
|
|
118
|
+
|
|
119
|
+
Re-score during execution when:
|
|
120
|
+
|
|
121
|
+
1. Scope expands into additional systems.
|
|
122
|
+
2. The initial approach fails and alternatives are needed.
|
|
123
|
+
3. User adds new constraints or quality requirements.
|
|
124
|
+
4. Gathered context is insufficient for a safe answer.
|
|
125
|
+
|
|
126
|
+
Escalate one tier when any of these materially changes risk or effort.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Quick Card
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
TRIVIAL (0-2) -> Minimal reasoning, direct answer
|
|
134
|
+
SIMPLE (3-5) -> Light reasoning, focused context
|
|
135
|
+
MODERATE (6-10) -> Selective reasoning, targeted exploration
|
|
136
|
+
COMPLEX (11-15) -> Regular reasoning, systematic analysis
|
|
137
|
+
EXPERT (16+) -> Deep reasoning, broad trade-off evaluation
|
|
138
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# {{projectName}} AI Agent Orchestrator
|
|
2
|
+
|
|
3
|
+
This file is generated by `skilly-hand`.
|
|
4
|
+
|
|
5
|
+
## Where
|
|
6
|
+
|
|
7
|
+
- Scope: repository root and all descendants unless overridden by deeper AGENTS files.
|
|
8
|
+
- Installation generated at: `{{generatedAt}}`.
|
|
9
|
+
- Detected technologies: `{{detectedTechnologies}}`.
|
|
10
|
+
|
|
11
|
+
## What
|
|
12
|
+
|
|
13
|
+
### Installed Skill Registry
|
|
14
|
+
|
|
15
|
+
| Skill | Purpose | Tags |
|
|
16
|
+
| ----- | ------- | ---- |
|
|
17
|
+
{{skillsTable}}
|
|
18
|
+
|
|
19
|
+
## When
|
|
20
|
+
|
|
21
|
+
### Auto-invoke Triggers
|
|
22
|
+
|
|
23
|
+
| Action | Skill |
|
|
24
|
+
| ------ | ----- |
|
|
25
|
+
{{autoInvokeTable}}
|
|
26
|
+
|
|
27
|
+
## Chaining Notations
|
|
28
|
+
|
|
29
|
+
Chaining notations document integrated workflows where multiple skills are sequentially invoked for complex tasks. Always invoke skills in documented order.
|
|
30
|
+
|
|
31
|
+
### Root AGENTS Maintenance Workflow
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
Updating root AGENTS.md guidance
|
|
35
|
+
-> agents-root-orchestrator
|
|
36
|
+
-> skill-creator (if reusable workflow docs changed)
|
|
37
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skilly-hand/skilly-hand",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "CC-BY-NC-4.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"skilly-hand": "packages/cli/src/bin.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"catalog",
|
|
14
|
+
"packages",
|
|
15
|
+
"README.md",
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"workspaces": [
|
|
20
|
+
"packages/*"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.0.0"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "node ./scripts/build-catalog-index.mjs",
|
|
27
|
+
"catalog:check": "node ./scripts/check-catalog.mjs",
|
|
28
|
+
"catalog:sync": "node ./scripts/sync-catalog-readme.mjs",
|
|
29
|
+
"agentic:self:sync": "node ./scripts/sync-self-agentic.mjs",
|
|
30
|
+
"test": "node --test tests/*.test.js",
|
|
31
|
+
"verify:packlist": "node ./scripts/verify-packlist.mjs",
|
|
32
|
+
"verify:publish": "npm run catalog:check && npm test && npm run verify:packlist",
|
|
33
|
+
"prepublishOnly": "npm run verify:publish",
|
|
34
|
+
"version": "node ./scripts/release-changelog.mjs",
|
|
35
|
+
"changelog:release": "node ./scripts/release-changelog.mjs",
|
|
36
|
+
"cli": "node ./packages/cli/src/bin.js",
|
|
37
|
+
"detect": "node ./packages/cli/src/bin.js detect",
|
|
38
|
+
"list": "node ./packages/cli/src/bin.js list",
|
|
39
|
+
"doctor": "node ./packages/cli/src/bin.js doctor"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const rootDir = path.resolve(__dirname, "../../..");
|
|
7
|
+
export const catalogDir = path.join(rootDir, "catalog");
|
|
8
|
+
export const skillsDir = path.join(catalogDir, "skills");
|
|
9
|
+
export const templatesDir = path.join(catalogDir, "templates");
|
|
10
|
+
|
|
11
|
+
const REQUIRED_FIELDS = [
|
|
12
|
+
"id",
|
|
13
|
+
"title",
|
|
14
|
+
"description",
|
|
15
|
+
"portable",
|
|
16
|
+
"tags",
|
|
17
|
+
"detectors",
|
|
18
|
+
"installsFor",
|
|
19
|
+
"agentSupport",
|
|
20
|
+
"files",
|
|
21
|
+
"dependencies"
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export function getCatalogRoot() {
|
|
25
|
+
return catalogDir;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function listSkillIds() {
|
|
29
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
30
|
+
return entries
|
|
31
|
+
.filter((entry) => entry.isDirectory())
|
|
32
|
+
.map((entry) => entry.name)
|
|
33
|
+
.sort();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function loadSkillManifest(skillId) {
|
|
37
|
+
const manifestPath = path.join(skillsDir, skillId, "manifest.json");
|
|
38
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
39
|
+
validateSkillManifest(manifest);
|
|
40
|
+
return manifest;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function loadAllSkills() {
|
|
44
|
+
const ids = await listSkillIds();
|
|
45
|
+
const manifests = [];
|
|
46
|
+
|
|
47
|
+
for (const skillId of ids) {
|
|
48
|
+
manifests.push(await loadSkillManifest(skillId));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return manifests;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function validateSkillManifest(manifest) {
|
|
55
|
+
for (const field of REQUIRED_FIELDS) {
|
|
56
|
+
if (!(field in manifest)) {
|
|
57
|
+
throw new Error(`Missing required field "${field}" in skill manifest`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!Array.isArray(manifest.tags) || manifest.tags.length === 0) {
|
|
62
|
+
throw new Error(`Skill "${manifest.id}" must declare at least one tag`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!Array.isArray(manifest.agentSupport) || manifest.agentSupport.length === 0) {
|
|
66
|
+
throw new Error(`Skill "${manifest.id}" must declare agentSupport`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
|
|
70
|
+
throw new Error(`Skill "${manifest.id}" must declare files`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function copySkillTo(targetCatalogDir, skillId) {
|
|
77
|
+
const source = path.join(skillsDir, skillId);
|
|
78
|
+
const destination = path.join(targetCatalogDir, skillId);
|
|
79
|
+
|
|
80
|
+
await mkdir(targetCatalogDir, { recursive: true });
|
|
81
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
82
|
+
|
|
83
|
+
return destination;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function readTemplate(templateName) {
|
|
87
|
+
return readFile(path.join(templatesDir, templateName), "utf8");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function renderAgentsMarkdown({ skills, detections, generatedAt, projectName }) {
|
|
91
|
+
const sortedSkills = [...skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
92
|
+
const sortedDetections = [...detections].sort((a, b) => a.technology.localeCompare(b.technology));
|
|
93
|
+
const autoInvokeSkills = sortedSkills.filter((skill) => skill.skillMetadata?.["auto-invoke"]);
|
|
94
|
+
|
|
95
|
+
const lines = [
|
|
96
|
+
"<!-- Managed by skilly-hand. Re-run `npx skilly-hand install` or `npm run agentic:self:sync` to regenerate. -->",
|
|
97
|
+
`# ${projectName || "Project"} AI Agent Orchestrator`,
|
|
98
|
+
"",
|
|
99
|
+
"> Root guidance for repository-wide AI routing. Use this file to understand where work belongs, what skills to invoke, and when triggers apply.",
|
|
100
|
+
"",
|
|
101
|
+
"## Where",
|
|
102
|
+
"",
|
|
103
|
+
"- Scope: repository root and all descendant folders unless a deeper AGENTS guide overrides locally.",
|
|
104
|
+
`- Generated at: ${generatedAt}`,
|
|
105
|
+
`- Detected technologies: ${sortedDetections.length === 0 ? "none" : sortedDetections.map((item) => item.technology).join(", ")}`,
|
|
106
|
+
"- Escalation boundary: when work changes global architecture, CI/CD, release, or security policy, escalate before implementation.",
|
|
107
|
+
"",
|
|
108
|
+
"## What",
|
|
109
|
+
"",
|
|
110
|
+
"### Installed Skill Registry",
|
|
111
|
+
"",
|
|
112
|
+
"| Skill | Description | Tags |",
|
|
113
|
+
"| ----- | ----------- | ---- |"
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
for (const skill of sortedSkills) {
|
|
117
|
+
lines.push(`| \`${skill.id}\` | ${skill.description} | ${skill.tags.join(", ")} |`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
lines.push(
|
|
121
|
+
"",
|
|
122
|
+
"### Task Routing",
|
|
123
|
+
"",
|
|
124
|
+
"**SDD-first policy:** for feature delivery, bug fixes, or any multi-step implementation, start with `spec-driven-development` unless the task is clearly trivial and one-step.",
|
|
125
|
+
"",
|
|
126
|
+
"| Task Type | Recommended Skill Chain |",
|
|
127
|
+
"| --------- | ----------------------- |",
|
|
128
|
+
"| Planning feature work, bug fixes, and multi-phase implementation | `spec-driven-development` |",
|
|
129
|
+
"| Executing approved implementation plans | `spec-driven-development` -> task-specific skills |",
|
|
130
|
+
"| Creating or updating reusable skills | `skill-creator` |",
|
|
131
|
+
"| Creating or updating root AGENTS orchestration guidance | `agents-root-orchestrator` |",
|
|
132
|
+
"",
|
|
133
|
+
"## When",
|
|
134
|
+
"",
|
|
135
|
+
"### Auto-invoke Triggers",
|
|
136
|
+
""
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (autoInvokeSkills.length === 0) {
|
|
140
|
+
lines.push("No explicit auto-invoke triggers were found in installed skill metadata.");
|
|
141
|
+
} else {
|
|
142
|
+
lines.push("| Action | Skill |", "| ------ | ----- |");
|
|
143
|
+
for (const skill of autoInvokeSkills) {
|
|
144
|
+
lines.push(`| ${skill.skillMetadata["auto-invoke"]} | \`${skill.id}\` |`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
lines.push(
|
|
149
|
+
"",
|
|
150
|
+
"### Sequencing Rules",
|
|
151
|
+
"",
|
|
152
|
+
"1. Classify task intent and scope first.",
|
|
153
|
+
"2. If work is non-trivial, invoke `spec-driven-development` before implementation.",
|
|
154
|
+
"3. Invoke prerequisite skills before implementation skills.",
|
|
155
|
+
"4. Verify outcomes and update this routing map when workflows change.",
|
|
156
|
+
"",
|
|
157
|
+
"## Chaining Notations",
|
|
158
|
+
"",
|
|
159
|
+
"Chaining notations document integrated workflows where multiple skills are sequentially invoked for complex tasks. Always invoke skills in documented order.",
|
|
160
|
+
"",
|
|
161
|
+
"### Root AGENTS Maintenance Workflow",
|
|
162
|
+
"",
|
|
163
|
+
"```text",
|
|
164
|
+
"Updating root AGENTS.md guidance",
|
|
165
|
+
" -> agents-root-orchestrator",
|
|
166
|
+
" -> skill-creator (if reusable workflow docs changed)",
|
|
167
|
+
"```",
|
|
168
|
+
"",
|
|
169
|
+
"### Skill Introduction Workflow",
|
|
170
|
+
"",
|
|
171
|
+
"```text",
|
|
172
|
+
"Asking for a new reusable skill",
|
|
173
|
+
" -> skill-creator",
|
|
174
|
+
" -> spec-driven-development",
|
|
175
|
+
" -> agents-root-orchestrator",
|
|
176
|
+
"```",
|
|
177
|
+
"",
|
|
178
|
+
"### SDD-First Delivery Workflow",
|
|
179
|
+
"",
|
|
180
|
+
"```text",
|
|
181
|
+
"Feature, bug, or multi-step execution request",
|
|
182
|
+
" -> spec-driven-development",
|
|
183
|
+
" -> task-specific implementation skill(s)",
|
|
184
|
+
" -> agents-root-orchestrator (if routing guidance changed)",
|
|
185
|
+
"```",
|
|
186
|
+
"",
|
|
187
|
+
"## Usage",
|
|
188
|
+
"",
|
|
189
|
+
"- Read the relevant `SKILL.md` file before starting a specialized task.",
|
|
190
|
+
"- Prefer installed skills under `.skilly-hand/catalog/` or repository `catalog/skills` as the source of truth.",
|
|
191
|
+
"- Use project-specific docs only as a supplement to these portable rules."
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return lines.join("\n") + "\n";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function writeCatalogIndex() {
|
|
198
|
+
const ids = await listSkillIds();
|
|
199
|
+
const outputPath = path.join(catalogDir, "catalog-index.json");
|
|
200
|
+
await writeFile(outputPath, JSON.stringify(ids, null, 2) + "\n", "utf8");
|
|
201
|
+
return outputPath;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function verifyCatalogFiles() {
|
|
205
|
+
const skillIds = await listSkillIds();
|
|
206
|
+
const issues = [];
|
|
207
|
+
|
|
208
|
+
for (const skillId of skillIds) {
|
|
209
|
+
const skillPath = path.join(skillsDir, skillId);
|
|
210
|
+
const manifest = await loadSkillManifest(skillId);
|
|
211
|
+
|
|
212
|
+
for (const file of manifest.files) {
|
|
213
|
+
const filePath = path.join(skillPath, file.path);
|
|
214
|
+
try {
|
|
215
|
+
await stat(filePath);
|
|
216
|
+
} catch {
|
|
217
|
+
issues.push(`Missing file for ${skillId}: ${file.path}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return issues;
|
|
223
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadAllSkills } from "../../catalog/src/index.js";
|
|
4
|
+
import { installProject, runDoctor, uninstallProject } from "../../core/src/index.js";
|
|
5
|
+
import { detectProject } from "../../detectors/src/index.js";
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv) {
|
|
8
|
+
const args = [...argv];
|
|
9
|
+
const positional = [];
|
|
10
|
+
const flags = {
|
|
11
|
+
dryRun: false,
|
|
12
|
+
yes: false,
|
|
13
|
+
verbose: false,
|
|
14
|
+
agents: [],
|
|
15
|
+
include: [],
|
|
16
|
+
exclude: []
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
while (args.length > 0) {
|
|
20
|
+
const token = args.shift();
|
|
21
|
+
if (!token.startsWith("-")) {
|
|
22
|
+
positional.push(token);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (token === "--dry-run") flags.dryRun = true;
|
|
27
|
+
else if (token === "--yes" || token === "-y") flags.yes = true;
|
|
28
|
+
else if (token === "--verbose" || token === "-v") flags.verbose = true;
|
|
29
|
+
else if (token === "--agent" || token === "-a") flags.agents.push(args.shift());
|
|
30
|
+
else if (token === "--cwd") flags.cwd = args.shift();
|
|
31
|
+
else if (token === "--include") flags.include.push(args.shift());
|
|
32
|
+
else if (token === "--exclude") flags.exclude.push(args.shift());
|
|
33
|
+
else if (token === "--help" || token === "-h") flags.help = true;
|
|
34
|
+
else throw new Error(`Unknown flag: ${token}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { command: positional[0], flags };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function printHelp() {
|
|
41
|
+
console.log(`skilly-hand
|
|
42
|
+
|
|
43
|
+
Usage:
|
|
44
|
+
npx skilly-hand [install]
|
|
45
|
+
npx skilly-hand detect
|
|
46
|
+
npx skilly-hand list
|
|
47
|
+
npx skilly-hand doctor
|
|
48
|
+
npx skilly-hand uninstall
|
|
49
|
+
|
|
50
|
+
Flags:
|
|
51
|
+
--dry-run
|
|
52
|
+
--yes
|
|
53
|
+
--verbose
|
|
54
|
+
--agent <codex|claude|cursor|gemini|copilot>
|
|
55
|
+
--cwd <path>
|
|
56
|
+
--include <tag>
|
|
57
|
+
--exclude <tag>
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function printDetections(detections) {
|
|
62
|
+
if (detections.length === 0) {
|
|
63
|
+
console.log("No technology signals were detected.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const item of detections) {
|
|
68
|
+
console.log(`- ${item.technology} (${item.confidence})`);
|
|
69
|
+
console.log(` reasons: ${item.reasons.join("; ")}`);
|
|
70
|
+
console.log(` recommended skills: ${item.recommendedSkillIds.join(", ")}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function printPlan(plan) {
|
|
75
|
+
console.log(`Project: ${plan.cwd}`);
|
|
76
|
+
console.log(`Install root: ${plan.installRoot}`);
|
|
77
|
+
console.log(`Agents: ${plan.agents.join(", ")}`);
|
|
78
|
+
console.log("Detected technologies:");
|
|
79
|
+
printDetections(plan.detections);
|
|
80
|
+
console.log("Skills to install:");
|
|
81
|
+
for (const skill of plan.skills) {
|
|
82
|
+
console.log(`- ${skill.id}: ${skill.title} [${skill.tags.join(", ")}]`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function main() {
|
|
87
|
+
const { command, flags } = parseArgs(process.argv.slice(2));
|
|
88
|
+
|
|
89
|
+
if (flags.help) {
|
|
90
|
+
printHelp();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const cwd = path.resolve(flags.cwd || process.cwd());
|
|
95
|
+
const effectiveCommand = command || "install";
|
|
96
|
+
|
|
97
|
+
if (effectiveCommand === "detect") {
|
|
98
|
+
printDetections(await detectProject(cwd));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (effectiveCommand === "list") {
|
|
103
|
+
const skills = await loadAllSkills();
|
|
104
|
+
for (const skill of skills) {
|
|
105
|
+
console.log(`- ${skill.id}: ${skill.title}`);
|
|
106
|
+
console.log(` tags: ${skill.tags.join(", ")}`);
|
|
107
|
+
console.log(` agents: ${skill.agentSupport.join(", ")}`);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (effectiveCommand === "doctor") {
|
|
113
|
+
const result = await runDoctor(cwd);
|
|
114
|
+
console.log(JSON.stringify(result, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (effectiveCommand === "uninstall") {
|
|
119
|
+
const result = await uninstallProject(cwd);
|
|
120
|
+
console.log(result.removed ? "skilly-hand installation removed." : result.reason);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (effectiveCommand === "install") {
|
|
125
|
+
const result = await installProject({
|
|
126
|
+
cwd,
|
|
127
|
+
agents: flags.agents,
|
|
128
|
+
dryRun: flags.dryRun,
|
|
129
|
+
includeTags: flags.include,
|
|
130
|
+
excludeTags: flags.exclude
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
printPlan(result.plan);
|
|
134
|
+
console.log(result.applied ? "Installation completed." : "Dry run only; nothing was written.");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new Error(`Unknown command: ${effectiveCommand}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
main().catch((error) => {
|
|
142
|
+
console.error(error.message);
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
});
|