@dzhechkov/harness-core 0.3.4 → 0.3.5

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/index.d.ts CHANGED
@@ -15,5 +15,7 @@ export type { CreateSkillOptions, CreateSkillResult } from './create-skill.js';
15
15
  export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
16
16
  export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from './sync-upstream.js';
17
17
  export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
18
+ export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
19
+ export type { RegistryEntry, Registry } from './registry.js';
18
20
  export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
19
21
  //# 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,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,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,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,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
@@ -13,4 +13,5 @@ export * from './workflows.js';
13
13
  export { createSkill } from './create-skill.js';
14
14
  export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
15
15
  export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
16
+ export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
16
17
  //# 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,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,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,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Skill registry — indexes all skills across skill packs for search and discovery.
3
+ *
4
+ * Scans `packages/@dzhechkov/skills-*` directories, reads SKILL.md frontmatter,
5
+ * and builds a searchable index.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /** A single skill in the registry. */
10
+ export interface RegistryEntry {
11
+ readonly id: string;
12
+ readonly pack: string;
13
+ readonly description: string;
14
+ readonly trustTier: number;
15
+ readonly hasSchema: boolean;
16
+ readonly hasEvals: boolean;
17
+ readonly lineCount: number;
18
+ readonly category: string;
19
+ }
20
+ /** The full registry. */
21
+ export interface Registry {
22
+ readonly entries: readonly RegistryEntry[];
23
+ readonly totalSkills: number;
24
+ readonly totalPacks: number;
25
+ readonly categories: readonly string[];
26
+ }
27
+ /** Build registry from all skills-* packages. */
28
+ export declare function buildRegistry(monorepoRoot: string): Registry;
29
+ /** Search registry by query (matches id and description, case-insensitive). */
30
+ export declare function searchRegistry(registry: Registry, query: string): readonly RegistryEntry[];
31
+ /** Filter registry by category. */
32
+ export declare function filterByCategory(registry: Registry, category: string): readonly RegistryEntry[];
33
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,yBAAyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAwBD,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAwC5D;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,aAAa,EAAE,CAK1F;AAED,mCAAmC;AACnC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,aAAa,EAAE,CAE/F"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Skill registry — indexes all skills across skill packs for search and discovery.
3
+ *
4
+ * Scans `packages/@dzhechkov/skills-*` directories, reads SKILL.md frontmatter,
5
+ * and builds a searchable index.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ /** Extract description from SKILL.md frontmatter. */
12
+ function extractFrontmatter(content) {
13
+ const descMatch = /^description:\s*"?(.+?)"?\s*$/m.exec(content);
14
+ const tierMatch = /^trust_tier:\s*(\d)/m.exec(content);
15
+ return {
16
+ description: descMatch?.[1] ?? '',
17
+ trustTier: tierMatch ? parseInt(tierMatch[1] ?? '1', 10) : 1,
18
+ };
19
+ }
20
+ /** Infer category from pack name. */
21
+ function categoryFromPack(pack) {
22
+ if (pack.includes('devops'))
23
+ return 'devops';
24
+ if (pack.includes('web3'))
25
+ return 'web3';
26
+ if (pack.includes('mcp'))
27
+ return 'mcp';
28
+ if (pack.includes('qe'))
29
+ return 'qe';
30
+ if (pack.includes('meta'))
31
+ return 'meta';
32
+ if (pack.includes('bto'))
33
+ return 'bto';
34
+ if (pack.includes('health'))
35
+ return 'health';
36
+ return 'other';
37
+ }
38
+ /** Build registry from all skills-* packages. */
39
+ export function buildRegistry(monorepoRoot) {
40
+ const baseDir = join(monorepoRoot, 'packages', '@dzhechkov');
41
+ if (!existsSync(baseDir))
42
+ return { entries: [], totalSkills: 0, totalPacks: 0, categories: [] };
43
+ const entries = [];
44
+ const packs = readdirSync(baseDir, { withFileTypes: true })
45
+ .filter((e) => e.isDirectory() && e.name.startsWith('skills-'));
46
+ for (const pack of packs) {
47
+ const packDir = join(baseDir, pack.name);
48
+ const skillDirs = readdirSync(packDir, { withFileTypes: true })
49
+ .filter((e) => e.isDirectory() && existsSync(join(packDir, e.name, 'SKILL.md')));
50
+ for (const skill of skillDirs) {
51
+ const skillMdPath = join(packDir, skill.name, 'SKILL.md');
52
+ const content = readFileSync(skillMdPath, 'utf-8');
53
+ const { description, trustTier } = extractFrontmatter(content);
54
+ entries.push({
55
+ id: skill.name,
56
+ pack: pack.name,
57
+ description,
58
+ trustTier,
59
+ hasSchema: existsSync(join(packDir, skill.name, 'schemas', 'output.json')),
60
+ hasEvals: existsSync(join(packDir, skill.name, 'evals')),
61
+ lineCount: content.split('\n').length,
62
+ category: categoryFromPack(pack.name),
63
+ });
64
+ }
65
+ }
66
+ entries.sort((a, b) => a.id.localeCompare(b.id));
67
+ const categories = [...new Set(entries.map((e) => e.category))].sort();
68
+ return {
69
+ entries,
70
+ totalSkills: entries.length,
71
+ totalPacks: packs.length,
72
+ categories,
73
+ };
74
+ }
75
+ /** Search registry by query (matches id and description, case-insensitive). */
76
+ export function searchRegistry(registry, query) {
77
+ const q = query.toLowerCase();
78
+ return registry.entries.filter((e) => e.id.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || e.category.includes(q));
79
+ }
80
+ /** Filter registry by category. */
81
+ export function filterByCategory(registry, category) {
82
+ return registry.entries.filter((e) => e.category === category);
83
+ }
84
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/harness-core",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
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",
@@ -25,12 +25,12 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "yaml": "^2.0.0",
28
+ "@dzhechkov/adapter-claude": "0.2.0",
28
29
  "@dzhechkov/adapter-hermes": "0.2.0",
29
30
  "@dzhechkov/adapter-opencode": "0.2.0",
30
- "@dzhechkov/adapter-claude": "0.2.0",
31
- "@dzhechkov/core": "0.2.0",
32
31
  "@dzhechkov/adapter-codex": "0.2.0",
33
- "@dzhechkov/adapter-openclaude": "0.1.0"
32
+ "@dzhechkov/adapter-openclaude": "0.1.0",
33
+ "@dzhechkov/core": "0.2.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^25.6.0",
package/src/index.ts CHANGED
@@ -17,4 +17,6 @@ export type { CreateSkillOptions, CreateSkillResult } from './create-skill.js';
17
17
  export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
18
18
  export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from './sync-upstream.js';
19
19
  export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
20
+ export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
21
+ export type { RegistryEntry, Registry } from './registry.js';
20
22
  export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Skill registry — indexes all skills across skill packs for search and discovery.
3
+ *
4
+ * Scans `packages/@dzhechkov/skills-*` directories, reads SKILL.md frontmatter,
5
+ * and builds a searchable index.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+
13
+ /** A single skill in the registry. */
14
+ export interface RegistryEntry {
15
+ readonly id: string;
16
+ readonly pack: string;
17
+ readonly description: string;
18
+ readonly trustTier: number;
19
+ readonly hasSchema: boolean;
20
+ readonly hasEvals: boolean;
21
+ readonly lineCount: number;
22
+ readonly category: string;
23
+ }
24
+
25
+ /** The full registry. */
26
+ export interface Registry {
27
+ readonly entries: readonly RegistryEntry[];
28
+ readonly totalSkills: number;
29
+ readonly totalPacks: number;
30
+ readonly categories: readonly string[];
31
+ }
32
+
33
+ /** Extract description from SKILL.md frontmatter. */
34
+ function extractFrontmatter(content: string): { description: string; trustTier: number } {
35
+ const descMatch = /^description:\s*"?(.+?)"?\s*$/m.exec(content);
36
+ const tierMatch = /^trust_tier:\s*(\d)/m.exec(content);
37
+ return {
38
+ description: descMatch?.[1] ?? '',
39
+ trustTier: tierMatch ? parseInt(tierMatch[1] ?? '1', 10) : 1,
40
+ };
41
+ }
42
+
43
+ /** Infer category from pack name. */
44
+ function categoryFromPack(pack: string): string {
45
+ if (pack.includes('devops')) return 'devops';
46
+ if (pack.includes('web3')) return 'web3';
47
+ if (pack.includes('mcp')) return 'mcp';
48
+ if (pack.includes('qe')) return 'qe';
49
+ if (pack.includes('meta')) return 'meta';
50
+ if (pack.includes('bto')) return 'bto';
51
+ if (pack.includes('health')) return 'health';
52
+ return 'other';
53
+ }
54
+
55
+ /** Build registry from all skills-* packages. */
56
+ export function buildRegistry(monorepoRoot: string): Registry {
57
+ const baseDir = join(monorepoRoot, 'packages', '@dzhechkov');
58
+ if (!existsSync(baseDir)) return { entries: [], totalSkills: 0, totalPacks: 0, categories: [] };
59
+
60
+ const entries: RegistryEntry[] = [];
61
+ const packs = readdirSync(baseDir, { withFileTypes: true })
62
+ .filter((e) => e.isDirectory() && e.name.startsWith('skills-'));
63
+
64
+ for (const pack of packs) {
65
+ const packDir = join(baseDir, pack.name);
66
+ const skillDirs = readdirSync(packDir, { withFileTypes: true })
67
+ .filter((e) => e.isDirectory() && existsSync(join(packDir, e.name, 'SKILL.md')));
68
+
69
+ for (const skill of skillDirs) {
70
+ const skillMdPath = join(packDir, skill.name, 'SKILL.md');
71
+ const content = readFileSync(skillMdPath, 'utf-8');
72
+ const { description, trustTier } = extractFrontmatter(content);
73
+
74
+ entries.push({
75
+ id: skill.name,
76
+ pack: pack.name,
77
+ description,
78
+ trustTier,
79
+ hasSchema: existsSync(join(packDir, skill.name, 'schemas', 'output.json')),
80
+ hasEvals: existsSync(join(packDir, skill.name, 'evals')),
81
+ lineCount: content.split('\n').length,
82
+ category: categoryFromPack(pack.name),
83
+ });
84
+ }
85
+ }
86
+
87
+ entries.sort((a, b) => a.id.localeCompare(b.id));
88
+ const categories = [...new Set(entries.map((e) => e.category))].sort();
89
+
90
+ return {
91
+ entries,
92
+ totalSkills: entries.length,
93
+ totalPacks: packs.length,
94
+ categories,
95
+ };
96
+ }
97
+
98
+ /** Search registry by query (matches id and description, case-insensitive). */
99
+ export function searchRegistry(registry: Registry, query: string): readonly RegistryEntry[] {
100
+ const q = query.toLowerCase();
101
+ return registry.entries.filter(
102
+ (e) => e.id.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || e.category.includes(q),
103
+ );
104
+ }
105
+
106
+ /** Filter registry by category. */
107
+ export function filterByCategory(registry: Registry, category: string): readonly RegistryEntry[] {
108
+ return registry.entries.filter((e) => e.category === category);
109
+ }