@dzhechkov/harness-core 0.3.16 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/import-ecc.d.ts +48 -0
- package/dist/import-ecc.d.ts.map +1 -0
- package/dist/import-ecc.js +171 -0
- package/dist/import-ecc.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/import-ecc.ts +219 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dz import-ecc` — import skills from the ECC project (https://github.com/affaan-m/ECC).
|
|
3
|
+
*
|
|
4
|
+
* Fetches SKILL.md files from ECC's GitHub repo, maps the frontmatter to
|
|
5
|
+
* agentskills.io format, deduplicates against existing skills, and writes
|
|
6
|
+
* the imported skills to the target directory.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/** Result of a single skill import. */
|
|
11
|
+
export interface ImportedSkill {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly status: 'imported' | 'skipped_duplicate' | 'skipped_error';
|
|
14
|
+
readonly reason?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Full import report. */
|
|
17
|
+
export interface ImportEccReport {
|
|
18
|
+
readonly source: string;
|
|
19
|
+
readonly totalFetched: number;
|
|
20
|
+
readonly imported: number;
|
|
21
|
+
readonly skippedDuplicate: number;
|
|
22
|
+
readonly skippedError: number;
|
|
23
|
+
readonly skills: readonly ImportedSkill[];
|
|
24
|
+
}
|
|
25
|
+
/** Options for the import. */
|
|
26
|
+
export interface ImportEccOptions {
|
|
27
|
+
/** Directory containing existing skills to check for duplicates. */
|
|
28
|
+
readonly existingSkillsDir: string;
|
|
29
|
+
/** Directory to write imported skills into. */
|
|
30
|
+
readonly outputDir: string;
|
|
31
|
+
/** If true, overwrite existing skills. */
|
|
32
|
+
readonly force?: boolean;
|
|
33
|
+
/** Only import these skill IDs (filter). */
|
|
34
|
+
readonly select?: readonly string[];
|
|
35
|
+
/** Maximum number of skills to import (default: all). */
|
|
36
|
+
readonly limit?: number;
|
|
37
|
+
/** Path to a local ECC clone (skip GitHub fetch). */
|
|
38
|
+
readonly localPath?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Import skills from ECC.
|
|
42
|
+
*
|
|
43
|
+
* Supports two modes:
|
|
44
|
+
* 1. Local clone: `--local-path /path/to/ECC` (fast, no network)
|
|
45
|
+
* 2. GitHub API: fetches from api.github.com (rate-limited, requires no clone)
|
|
46
|
+
*/
|
|
47
|
+
export declare function importEcc(options: ImportEccOptions): Promise<ImportEccReport>;
|
|
48
|
+
//# sourceMappingURL=import-ecc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-ecc.d.ts","sourceRoot":"","sources":["../src/import-ecc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,mBAAmB,GAAG,eAAe,CAAC;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0BAA0B;AAC1B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;CAC3C;AAED,8BAA8B;AAC9B,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AA6ED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0FnF"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dz import-ecc` — import skills from the ECC project (https://github.com/affaan-m/ECC).
|
|
3
|
+
*
|
|
4
|
+
* Fetches SKILL.md files from ECC's GitHub repo, maps the frontmatter to
|
|
5
|
+
* agentskills.io format, deduplicates against existing skills, and writes
|
|
6
|
+
* the imported skills to the target directory.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
const ECC_SKILLS_API = 'https://api.github.com/repos/affaan-m/ECC/contents/skills';
|
|
13
|
+
const ECC_RAW_BASE = 'https://raw.githubusercontent.com/affaan-m/ECC/main/skills';
|
|
14
|
+
/**
|
|
15
|
+
* Map ECC YAML frontmatter to agentskills.io format.
|
|
16
|
+
*
|
|
17
|
+
* ECC format:
|
|
18
|
+
* ```yaml
|
|
19
|
+
* name: skill-name
|
|
20
|
+
* description: long description
|
|
21
|
+
* origin: ECC
|
|
22
|
+
* tools: Read, Write, Bash
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* agentskills.io format:
|
|
26
|
+
* ```yaml
|
|
27
|
+
* name: skill-name
|
|
28
|
+
* description: long description
|
|
29
|
+
* trust_tier: 0
|
|
30
|
+
* trust_tier_label: "Community (imported from ECC)"
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
function mapFrontmatter(eccContent) {
|
|
34
|
+
// Extract frontmatter
|
|
35
|
+
const fmMatch = eccContent.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
36
|
+
if (!fmMatch)
|
|
37
|
+
return eccContent; // No frontmatter — return as-is
|
|
38
|
+
const fmYaml = fmMatch[1] ?? '';
|
|
39
|
+
const body = eccContent.slice(fmMatch[0]?.length ?? 0);
|
|
40
|
+
// Parse fields from YAML (simple key: value parsing, no library needed)
|
|
41
|
+
const nameMatch = fmYaml.match(/^name:\s*(.+)$/m);
|
|
42
|
+
const descMatch = fmYaml.match(/^description:\s*([\s\S]*?)(?=\n\w+:|$)/m);
|
|
43
|
+
const name = nameMatch?.[1]?.trim() ?? 'unknown';
|
|
44
|
+
let desc = descMatch?.[1]?.trim() ?? '';
|
|
45
|
+
// Collapse multi-line description
|
|
46
|
+
desc = desc.replace(/\n\s+/g, ' ');
|
|
47
|
+
// Build agentskills.io frontmatter
|
|
48
|
+
const newFm = [
|
|
49
|
+
'---',
|
|
50
|
+
`name: "${name}"`,
|
|
51
|
+
`description: >`,
|
|
52
|
+
` ${desc}`,
|
|
53
|
+
`trust_tier: 0`,
|
|
54
|
+
`trust_tier_label: "Community (imported from ECC)"`,
|
|
55
|
+
`source: "https://github.com/affaan-m/ECC/tree/main/skills/${name}"`,
|
|
56
|
+
'---',
|
|
57
|
+
].join('\n');
|
|
58
|
+
return newFm + '\n' + body;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* List ECC skill directories from a local clone.
|
|
62
|
+
*/
|
|
63
|
+
function listLocalSkills(localPath) {
|
|
64
|
+
const skillsDir = join(localPath, 'skills');
|
|
65
|
+
if (!existsSync(skillsDir))
|
|
66
|
+
return [];
|
|
67
|
+
return readdirSync(skillsDir, { withFileTypes: true })
|
|
68
|
+
.filter((d) => d.isDirectory())
|
|
69
|
+
.map((d) => d.name)
|
|
70
|
+
.sort();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read a skill from a local ECC clone.
|
|
74
|
+
*/
|
|
75
|
+
function readLocalSkill(localPath, skillId) {
|
|
76
|
+
const skillMd = join(localPath, 'skills', skillId, 'SKILL.md');
|
|
77
|
+
if (!existsSync(skillMd))
|
|
78
|
+
return null;
|
|
79
|
+
return readFileSync(skillMd, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Import skills from ECC.
|
|
83
|
+
*
|
|
84
|
+
* Supports two modes:
|
|
85
|
+
* 1. Local clone: `--local-path /path/to/ECC` (fast, no network)
|
|
86
|
+
* 2. GitHub API: fetches from api.github.com (rate-limited, requires no clone)
|
|
87
|
+
*/
|
|
88
|
+
export async function importEcc(options) {
|
|
89
|
+
const existing = new Set();
|
|
90
|
+
if (existsSync(options.existingSkillsDir)) {
|
|
91
|
+
for (const d of readdirSync(options.existingSkillsDir, { withFileTypes: true })) {
|
|
92
|
+
if (d.isDirectory())
|
|
93
|
+
existing.add(d.name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
let skillIds;
|
|
97
|
+
if (options.localPath) {
|
|
98
|
+
// Local clone mode
|
|
99
|
+
skillIds = listLocalSkills(options.localPath);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// GitHub API mode — fetch directory listing
|
|
103
|
+
const res = await fetch(ECC_SKILLS_API, {
|
|
104
|
+
headers: { 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'dz-harness-hub' },
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
return {
|
|
108
|
+
source: 'github.com/affaan-m/ECC',
|
|
109
|
+
totalFetched: 0, imported: 0, skippedDuplicate: 0, skippedError: 1,
|
|
110
|
+
skills: [{ id: 'api', status: 'skipped_error', reason: `GitHub API ${res.status}: ${res.statusText}` }],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const entries = (await res.json());
|
|
114
|
+
skillIds = entries.filter((e) => e.type === 'dir').map((e) => e.name).sort();
|
|
115
|
+
}
|
|
116
|
+
// Apply filters
|
|
117
|
+
if (options.select) {
|
|
118
|
+
const sel = new Set(options.select);
|
|
119
|
+
skillIds = skillIds.filter((id) => sel.has(id));
|
|
120
|
+
}
|
|
121
|
+
if (options.limit) {
|
|
122
|
+
skillIds = skillIds.slice(0, options.limit);
|
|
123
|
+
}
|
|
124
|
+
const results = [];
|
|
125
|
+
for (const id of skillIds) {
|
|
126
|
+
// Check for duplicates
|
|
127
|
+
if (existing.has(id) && !options.force) {
|
|
128
|
+
results.push({ id, status: 'skipped_duplicate', reason: `Already exists in ${options.existingSkillsDir}` });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
let content;
|
|
133
|
+
if (options.localPath) {
|
|
134
|
+
content = readLocalSkill(options.localPath, id);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Fetch from GitHub raw
|
|
138
|
+
const url = `${ECC_RAW_BASE}/${id}/SKILL.md`;
|
|
139
|
+
const res = await fetch(url, { headers: { 'User-Agent': 'dz-harness-hub' } });
|
|
140
|
+
if (!res.ok) {
|
|
141
|
+
results.push({ id, status: 'skipped_error', reason: `HTTP ${res.status}` });
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
content = await res.text();
|
|
145
|
+
}
|
|
146
|
+
if (!content) {
|
|
147
|
+
results.push({ id, status: 'skipped_error', reason: 'No SKILL.md found' });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Map frontmatter
|
|
151
|
+
const mapped = mapFrontmatter(content);
|
|
152
|
+
// Write to output directory
|
|
153
|
+
const outDir = join(options.outputDir, id);
|
|
154
|
+
mkdirSync(outDir, { recursive: true });
|
|
155
|
+
writeFileSync(join(outDir, 'SKILL.md'), mapped, 'utf-8');
|
|
156
|
+
results.push({ id, status: 'imported' });
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
results.push({ id, status: 'skipped_error', reason: String(err) });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
source: options.localPath ?? 'github.com/affaan-m/ECC',
|
|
164
|
+
totalFetched: skillIds.length,
|
|
165
|
+
imported: results.filter((r) => r.status === 'imported').length,
|
|
166
|
+
skippedDuplicate: results.filter((r) => r.status === 'skipped_duplicate').length,
|
|
167
|
+
skippedError: results.filter((r) => r.status === 'skipped_error').length,
|
|
168
|
+
skills: results,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=import-ecc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-ecc.js","sourceRoot":"","sources":["../src/import-ecc.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export type { InstalledSkill, UpgradeCheck, UpgradeReport } from './upgrade.js';
|
|
|
32
32
|
export type { PublishResult, PublishReport } from './publish.js';
|
|
33
33
|
export { computeRiskScore } from './risk-scoring.js';
|
|
34
34
|
export type { RiskScore, RiskThresholds } from './risk-scoring.js';
|
|
35
|
+
export { importEcc } from './import-ecc.js';
|
|
36
|
+
export type { ImportEccReport, ImportEccOptions, ImportedSkill } from './import-ecc.js';
|
|
35
37
|
export type { RegistryEntry, Registry } from './registry.js';
|
|
36
38
|
export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
|
|
37
39
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAClH,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAClH,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxF,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -22,4 +22,5 @@ export { discoverPackages, publishPackages } from './publish.js';
|
|
|
22
22
|
export { fetchAllDownloads } from './downloads.js';
|
|
23
23
|
export { discoverInstalled, checkUpgrades } from './upgrade.js';
|
|
24
24
|
export { computeRiskScore } from './risk-scoring.js';
|
|
25
|
+
export { importEcc } from './import-ecc.js';
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAElH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yDAAyD;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAElH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "Shared harness logic - skill loading, additive apply, and the init/sync/verify/doctor operations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"lint": "tsc -p tsconfig.json --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@dzhechkov/adapter-claude": "
|
|
35
|
-
"@dzhechkov/adapter-codex": "
|
|
36
|
-
"@dzhechkov/adapter-hermes": "
|
|
37
|
-
"@dzhechkov/adapter-openclaude": "
|
|
38
|
-
"@dzhechkov/adapter-opencode": "
|
|
39
|
-
"@dzhechkov/core": "
|
|
34
|
+
"@dzhechkov/adapter-claude": "^0.2.0",
|
|
35
|
+
"@dzhechkov/adapter-codex": "^0.2.0",
|
|
36
|
+
"@dzhechkov/adapter-hermes": "^0.2.0",
|
|
37
|
+
"@dzhechkov/adapter-openclaude": "^0.1.0",
|
|
38
|
+
"@dzhechkov/adapter-opencode": "^0.2.0",
|
|
39
|
+
"@dzhechkov/core": "^0.2.0",
|
|
40
40
|
"yaml": "^2.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dz import-ecc` — import skills from the ECC project (https://github.com/affaan-m/ECC).
|
|
3
|
+
*
|
|
4
|
+
* Fetches SKILL.md files from ECC's GitHub repo, maps the frontmatter to
|
|
5
|
+
* agentskills.io format, deduplicates against existing skills, and writes
|
|
6
|
+
* the imported skills to the target directory.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
/** Result of a single skill import. */
|
|
15
|
+
export interface ImportedSkill {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly status: 'imported' | 'skipped_duplicate' | 'skipped_error';
|
|
18
|
+
readonly reason?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Full import report. */
|
|
22
|
+
export interface ImportEccReport {
|
|
23
|
+
readonly source: string;
|
|
24
|
+
readonly totalFetched: number;
|
|
25
|
+
readonly imported: number;
|
|
26
|
+
readonly skippedDuplicate: number;
|
|
27
|
+
readonly skippedError: number;
|
|
28
|
+
readonly skills: readonly ImportedSkill[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Options for the import. */
|
|
32
|
+
export interface ImportEccOptions {
|
|
33
|
+
/** Directory containing existing skills to check for duplicates. */
|
|
34
|
+
readonly existingSkillsDir: string;
|
|
35
|
+
/** Directory to write imported skills into. */
|
|
36
|
+
readonly outputDir: string;
|
|
37
|
+
/** If true, overwrite existing skills. */
|
|
38
|
+
readonly force?: boolean;
|
|
39
|
+
/** Only import these skill IDs (filter). */
|
|
40
|
+
readonly select?: readonly string[];
|
|
41
|
+
/** Maximum number of skills to import (default: all). */
|
|
42
|
+
readonly limit?: number;
|
|
43
|
+
/** Path to a local ECC clone (skip GitHub fetch). */
|
|
44
|
+
readonly localPath?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const ECC_SKILLS_API = 'https://api.github.com/repos/affaan-m/ECC/contents/skills';
|
|
48
|
+
const ECC_RAW_BASE = 'https://raw.githubusercontent.com/affaan-m/ECC/main/skills';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Map ECC YAML frontmatter to agentskills.io format.
|
|
52
|
+
*
|
|
53
|
+
* ECC format:
|
|
54
|
+
* ```yaml
|
|
55
|
+
* name: skill-name
|
|
56
|
+
* description: long description
|
|
57
|
+
* origin: ECC
|
|
58
|
+
* tools: Read, Write, Bash
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* agentskills.io format:
|
|
62
|
+
* ```yaml
|
|
63
|
+
* name: skill-name
|
|
64
|
+
* description: long description
|
|
65
|
+
* trust_tier: 0
|
|
66
|
+
* trust_tier_label: "Community (imported from ECC)"
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
function mapFrontmatter(eccContent: string): string {
|
|
70
|
+
// Extract frontmatter
|
|
71
|
+
const fmMatch = eccContent.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
72
|
+
if (!fmMatch) return eccContent; // No frontmatter — return as-is
|
|
73
|
+
|
|
74
|
+
const fmYaml = fmMatch[1] ?? '';
|
|
75
|
+
const body = eccContent.slice(fmMatch[0]?.length ?? 0);
|
|
76
|
+
|
|
77
|
+
// Parse fields from YAML (simple key: value parsing, no library needed)
|
|
78
|
+
const nameMatch = fmYaml.match(/^name:\s*(.+)$/m);
|
|
79
|
+
const descMatch = fmYaml.match(/^description:\s*([\s\S]*?)(?=\n\w+:|$)/m);
|
|
80
|
+
|
|
81
|
+
const name = nameMatch?.[1]?.trim() ?? 'unknown';
|
|
82
|
+
let desc = descMatch?.[1]?.trim() ?? '';
|
|
83
|
+
// Collapse multi-line description
|
|
84
|
+
desc = desc.replace(/\n\s+/g, ' ');
|
|
85
|
+
|
|
86
|
+
// Build agentskills.io frontmatter
|
|
87
|
+
const newFm = [
|
|
88
|
+
'---',
|
|
89
|
+
`name: "${name}"`,
|
|
90
|
+
`description: >`,
|
|
91
|
+
` ${desc}`,
|
|
92
|
+
`trust_tier: 0`,
|
|
93
|
+
`trust_tier_label: "Community (imported from ECC)"`,
|
|
94
|
+
`source: "https://github.com/affaan-m/ECC/tree/main/skills/${name}"`,
|
|
95
|
+
'---',
|
|
96
|
+
].join('\n');
|
|
97
|
+
|
|
98
|
+
return newFm + '\n' + body;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* List ECC skill directories from a local clone.
|
|
103
|
+
*/
|
|
104
|
+
function listLocalSkills(localPath: string): string[] {
|
|
105
|
+
const skillsDir = join(localPath, 'skills');
|
|
106
|
+
if (!existsSync(skillsDir)) return [];
|
|
107
|
+
return readdirSync(skillsDir, { withFileTypes: true })
|
|
108
|
+
.filter((d) => d.isDirectory())
|
|
109
|
+
.map((d) => d.name)
|
|
110
|
+
.sort();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Read a skill from a local ECC clone.
|
|
115
|
+
*/
|
|
116
|
+
function readLocalSkill(localPath: string, skillId: string): string | null {
|
|
117
|
+
const skillMd = join(localPath, 'skills', skillId, 'SKILL.md');
|
|
118
|
+
if (!existsSync(skillMd)) return null;
|
|
119
|
+
return readFileSync(skillMd, 'utf-8');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Import skills from ECC.
|
|
124
|
+
*
|
|
125
|
+
* Supports two modes:
|
|
126
|
+
* 1. Local clone: `--local-path /path/to/ECC` (fast, no network)
|
|
127
|
+
* 2. GitHub API: fetches from api.github.com (rate-limited, requires no clone)
|
|
128
|
+
*/
|
|
129
|
+
export async function importEcc(options: ImportEccOptions): Promise<ImportEccReport> {
|
|
130
|
+
const existing = new Set<string>();
|
|
131
|
+
if (existsSync(options.existingSkillsDir)) {
|
|
132
|
+
for (const d of readdirSync(options.existingSkillsDir, { withFileTypes: true })) {
|
|
133
|
+
if (d.isDirectory()) existing.add(d.name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let skillIds: string[];
|
|
138
|
+
|
|
139
|
+
if (options.localPath) {
|
|
140
|
+
// Local clone mode
|
|
141
|
+
skillIds = listLocalSkills(options.localPath);
|
|
142
|
+
} else {
|
|
143
|
+
// GitHub API mode — fetch directory listing
|
|
144
|
+
const res = await fetch(ECC_SKILLS_API, {
|
|
145
|
+
headers: { 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'dz-harness-hub' },
|
|
146
|
+
});
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
return {
|
|
149
|
+
source: 'github.com/affaan-m/ECC',
|
|
150
|
+
totalFetched: 0, imported: 0, skippedDuplicate: 0, skippedError: 1,
|
|
151
|
+
skills: [{ id: 'api', status: 'skipped_error', reason: `GitHub API ${res.status}: ${res.statusText}` }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const entries = (await res.json()) as { name: string; type: string }[];
|
|
155
|
+
skillIds = entries.filter((e) => e.type === 'dir').map((e) => e.name).sort();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Apply filters
|
|
159
|
+
if (options.select) {
|
|
160
|
+
const sel = new Set(options.select);
|
|
161
|
+
skillIds = skillIds.filter((id) => sel.has(id));
|
|
162
|
+
}
|
|
163
|
+
if (options.limit) {
|
|
164
|
+
skillIds = skillIds.slice(0, options.limit);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const results: ImportedSkill[] = [];
|
|
168
|
+
|
|
169
|
+
for (const id of skillIds) {
|
|
170
|
+
// Check for duplicates
|
|
171
|
+
if (existing.has(id) && !options.force) {
|
|
172
|
+
results.push({ id, status: 'skipped_duplicate', reason: `Already exists in ${options.existingSkillsDir}` });
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
let content: string | null;
|
|
178
|
+
|
|
179
|
+
if (options.localPath) {
|
|
180
|
+
content = readLocalSkill(options.localPath, id);
|
|
181
|
+
} else {
|
|
182
|
+
// Fetch from GitHub raw
|
|
183
|
+
const url = `${ECC_RAW_BASE}/${id}/SKILL.md`;
|
|
184
|
+
const res = await fetch(url, { headers: { 'User-Agent': 'dz-harness-hub' } });
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
results.push({ id, status: 'skipped_error', reason: `HTTP ${res.status}` });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
content = await res.text();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!content) {
|
|
193
|
+
results.push({ id, status: 'skipped_error', reason: 'No SKILL.md found' });
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Map frontmatter
|
|
198
|
+
const mapped = mapFrontmatter(content);
|
|
199
|
+
|
|
200
|
+
// Write to output directory
|
|
201
|
+
const outDir = join(options.outputDir, id);
|
|
202
|
+
mkdirSync(outDir, { recursive: true });
|
|
203
|
+
writeFileSync(join(outDir, 'SKILL.md'), mapped, 'utf-8');
|
|
204
|
+
|
|
205
|
+
results.push({ id, status: 'imported' });
|
|
206
|
+
} catch (err) {
|
|
207
|
+
results.push({ id, status: 'skipped_error', reason: String(err) });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
source: options.localPath ?? 'github.com/affaan-m/ECC',
|
|
213
|
+
totalFetched: skillIds.length,
|
|
214
|
+
imported: results.filter((r) => r.status === 'imported').length,
|
|
215
|
+
skippedDuplicate: results.filter((r) => r.status === 'skipped_duplicate').length,
|
|
216
|
+
skippedError: results.filter((r) => r.status === 'skipped_error').length,
|
|
217
|
+
skills: results,
|
|
218
|
+
};
|
|
219
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -34,5 +34,7 @@ export type { InstalledSkill, UpgradeCheck, UpgradeReport } from './upgrade.js';
|
|
|
34
34
|
export type { PublishResult, PublishReport } from './publish.js';
|
|
35
35
|
export { computeRiskScore } from './risk-scoring.js';
|
|
36
36
|
export type { RiskScore, RiskThresholds } from './risk-scoring.js';
|
|
37
|
+
export { importEcc } from './import-ecc.js';
|
|
38
|
+
export type { ImportEccReport, ImportEccOptions, ImportedSkill } from './import-ecc.js';
|
|
37
39
|
export type { RegistryEntry, Registry } from './registry.js';
|
|
38
40
|
export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
|