@dzhechkov/harness-core 0.3.16 → 0.3.17

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.
@@ -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
@@ -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.16",
3
+ "version": "0.3.17",
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",
@@ -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';