@dzhechkov/harness-core 0.3.6 → 0.3.7
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/downloads.d.ts +22 -0
- package/dist/downloads.d.ts.map +1 -0
- package/dist/downloads.js +36 -0
- package/dist/downloads.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/upgrade.d.ts +35 -0
- package/dist/upgrade.d.ts.map +1 -0
- package/dist/upgrade.js +77 -0
- package/dist/upgrade.js.map +1 -0
- package/package.json +3 -3
- package/src/downloads.ts +54 -0
- package/src/index.ts +4 -0
- package/src/upgrade.ts +113 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm download statistics — fetches weekly download counts for all published packages.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Download stats for a single package. */
|
|
7
|
+
export interface PackageDownloads {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly downloads: number;
|
|
10
|
+
readonly period: string;
|
|
11
|
+
readonly error?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Full downloads report. */
|
|
14
|
+
export interface DownloadsReport {
|
|
15
|
+
readonly packages: readonly PackageDownloads[];
|
|
16
|
+
readonly totalDownloads: number;
|
|
17
|
+
readonly period: string;
|
|
18
|
+
readonly fetchedAt: string;
|
|
19
|
+
}
|
|
20
|
+
/** Fetch downloads for all given package names. */
|
|
21
|
+
export declare function fetchAllDownloads(packageNames: string[]): Promise<DownloadsReport>;
|
|
22
|
+
//# sourceMappingURL=downloads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloads.d.ts","sourceRoot":"","sources":["../src/downloads.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,6BAA6B;AAC7B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAuBD,mDAAmD;AACnD,wBAAsB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CASxF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm download statistics — fetches weekly download counts for all published packages.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Fetch weekly downloads for a single npm package. */
|
|
7
|
+
async function fetchDownloads(name) {
|
|
8
|
+
const period = 'last-week';
|
|
9
|
+
try {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timer = setTimeout(() => { controller.abort(); }, 10000);
|
|
12
|
+
const url = `https://api.npmjs.org/downloads/point/${period}/${encodeURIComponent(name)}`;
|
|
13
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
if (!resp.ok) {
|
|
16
|
+
return { name, downloads: 0, period, error: `HTTP ${resp.status}` };
|
|
17
|
+
}
|
|
18
|
+
const data = (await resp.json());
|
|
19
|
+
return { name, downloads: data.downloads, period };
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return { name, downloads: 0, period, error: err instanceof Error ? err.message : String(err) };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Fetch downloads for all given package names. */
|
|
26
|
+
export async function fetchAllDownloads(packageNames) {
|
|
27
|
+
const results = await Promise.all(packageNames.map(fetchDownloads));
|
|
28
|
+
results.sort((a, b) => b.downloads - a.downloads);
|
|
29
|
+
return {
|
|
30
|
+
packages: results,
|
|
31
|
+
totalDownloads: results.reduce((sum, r) => sum + r.downloads, 0),
|
|
32
|
+
period: 'last-week',
|
|
33
|
+
fetchedAt: new Date().toISOString(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=downloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloads.js","sourceRoot":"","sources":["../src/downloads.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,uDAAuD;AACvD,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,yCAAyC,MAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACtE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAA0B,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACjG,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,YAAsB;IAC5D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAClD,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QAChE,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from '.
|
|
|
17
17
|
export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
|
|
18
18
|
export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
|
|
19
19
|
export { discoverPackages, publishPackages } from './publish.js';
|
|
20
|
+
export { fetchAllDownloads } from './downloads.js';
|
|
21
|
+
export type { PackageDownloads, DownloadsReport } from './downloads.js';
|
|
22
|
+
export { discoverInstalled, checkUpgrades } from './upgrade.js';
|
|
23
|
+
export type { InstalledSkill, UpgradeCheck, UpgradeReport } from './upgrade.js';
|
|
20
24
|
export type { PublishResult, PublishReport } from './publish.js';
|
|
21
25
|
export type { RegistryEntry, Registry } from './registry.js';
|
|
22
26
|
export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
|
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,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,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,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,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,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,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
|
@@ -15,4 +15,6 @@ export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
|
|
|
15
15
|
export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
|
|
16
16
|
export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
|
|
17
17
|
export { discoverPackages, publishPackages } from './publish.js';
|
|
18
|
+
export { fetchAllDownloads } from './downloads.js';
|
|
19
|
+
export { discoverInstalled, checkUpgrades } from './upgrade.js';
|
|
18
20
|
//# 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;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,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;AAChF,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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill upgrade — detects installed skills and re-applies from canonical source.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Info about an installed skill. */
|
|
7
|
+
export interface InstalledSkill {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly size: number;
|
|
11
|
+
readonly modifiedAt: string;
|
|
12
|
+
readonly hasFrontmatter: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Upgrade check for a single skill. */
|
|
15
|
+
export interface UpgradeCheck {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly installed: InstalledSkill;
|
|
18
|
+
readonly canonicalSize: number | undefined;
|
|
19
|
+
readonly needsUpdate: boolean;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
}
|
|
22
|
+
/** Full upgrade report. */
|
|
23
|
+
export interface UpgradeReport {
|
|
24
|
+
readonly targetDir: string;
|
|
25
|
+
readonly installed: number;
|
|
26
|
+
readonly needsUpdate: number;
|
|
27
|
+
readonly upToDate: number;
|
|
28
|
+
readonly notInCanonical: number;
|
|
29
|
+
readonly skills: readonly UpgradeCheck[];
|
|
30
|
+
}
|
|
31
|
+
/** Discover installed skills in a target platform directory. */
|
|
32
|
+
export declare function discoverInstalled(targetDir: string): InstalledSkill[];
|
|
33
|
+
/** Check which installed skills need updates by comparing with canonical sources. */
|
|
34
|
+
export declare function checkUpgrades(targetDir: string, canonicalDirs: string[]): UpgradeReport;
|
|
35
|
+
//# sourceMappingURL=upgrade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,qCAAqC;AACrC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,2BAA2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;CAC1C;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,CAgBrE;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EAAE,GACtB,aAAa,CAoDf"}
|
package/dist/upgrade.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill upgrade — detects installed skills and re-applies from canonical source.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
/** Discover installed skills in a target platform directory. */
|
|
9
|
+
export function discoverInstalled(targetDir) {
|
|
10
|
+
if (!existsSync(targetDir))
|
|
11
|
+
return [];
|
|
12
|
+
return readdirSync(targetDir, { withFileTypes: true })
|
|
13
|
+
.filter((e) => e.isDirectory() && existsSync(join(targetDir, e.name, 'SKILL.md')))
|
|
14
|
+
.map((e) => {
|
|
15
|
+
const skillMdPath = join(targetDir, e.name, 'SKILL.md');
|
|
16
|
+
const stat = statSync(skillMdPath);
|
|
17
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
18
|
+
return {
|
|
19
|
+
id: e.name,
|
|
20
|
+
path: join(targetDir, e.name),
|
|
21
|
+
size: stat.size,
|
|
22
|
+
modifiedAt: stat.mtime.toISOString().slice(0, 10),
|
|
23
|
+
hasFrontmatter: content.startsWith('---'),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Check which installed skills need updates by comparing with canonical sources. */
|
|
28
|
+
export function checkUpgrades(targetDir, canonicalDirs) {
|
|
29
|
+
const installed = discoverInstalled(targetDir);
|
|
30
|
+
// Build canonical index: skill id → { dir, size }
|
|
31
|
+
const canonical = new Map();
|
|
32
|
+
for (const cDir of canonicalDirs) {
|
|
33
|
+
if (!existsSync(cDir))
|
|
34
|
+
continue;
|
|
35
|
+
const skills = readdirSync(cDir, { withFileTypes: true })
|
|
36
|
+
.filter((e) => e.isDirectory() && existsSync(join(cDir, e.name, 'SKILL.md')));
|
|
37
|
+
for (const skill of skills) {
|
|
38
|
+
const skillMdPath = join(cDir, skill.name, 'SKILL.md');
|
|
39
|
+
const stat = statSync(skillMdPath);
|
|
40
|
+
canonical.set(skill.name, { dir: join(cDir, skill.name), size: stat.size });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const checks = [];
|
|
44
|
+
for (const inst of installed) {
|
|
45
|
+
const can = canonical.get(inst.id);
|
|
46
|
+
if (!can) {
|
|
47
|
+
checks.push({
|
|
48
|
+
id: inst.id,
|
|
49
|
+
installed: inst,
|
|
50
|
+
canonicalSize: undefined,
|
|
51
|
+
needsUpdate: false,
|
|
52
|
+
reason: 'not in canonical (custom or removed)',
|
|
53
|
+
});
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const sizeDiff = Math.abs(inst.size - can.size);
|
|
57
|
+
const needsUpdate = sizeDiff > 0;
|
|
58
|
+
checks.push({
|
|
59
|
+
id: inst.id,
|
|
60
|
+
installed: inst,
|
|
61
|
+
canonicalSize: can.size,
|
|
62
|
+
needsUpdate,
|
|
63
|
+
reason: needsUpdate
|
|
64
|
+
? `size differs (installed: ${inst.size}B, canonical: ${can.size}B, delta: ${sizeDiff}B)`
|
|
65
|
+
: 'up to date',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
targetDir,
|
|
70
|
+
installed: installed.length,
|
|
71
|
+
needsUpdate: checks.filter((c) => c.needsUpdate).length,
|
|
72
|
+
upToDate: checks.filter((c) => !c.needsUpdate && c.canonicalSize !== undefined).length,
|
|
73
|
+
notInCanonical: checks.filter((c) => c.canonicalSize === undefined).length,
|
|
74
|
+
skills: checks,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.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.
|
|
3
|
+
"version": "0.3.7",
|
|
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",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"yaml": "^2.0.0",
|
|
28
28
|
"@dzhechkov/adapter-claude": "0.2.0",
|
|
29
|
+
"@dzhechkov/adapter-codex": "0.2.0",
|
|
29
30
|
"@dzhechkov/adapter-hermes": "0.2.0",
|
|
30
|
-
"@dzhechkov/core": "0.2.0",
|
|
31
31
|
"@dzhechkov/adapter-opencode": "0.2.0",
|
|
32
32
|
"@dzhechkov/adapter-openclaude": "0.1.0",
|
|
33
|
-
"@dzhechkov/
|
|
33
|
+
"@dzhechkov/core": "0.2.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^25.6.0",
|
package/src/downloads.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm download statistics — fetches weekly download counts for all published packages.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Download stats for a single package. */
|
|
8
|
+
export interface PackageDownloads {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly downloads: number;
|
|
11
|
+
readonly period: string;
|
|
12
|
+
readonly error?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Full downloads report. */
|
|
16
|
+
export interface DownloadsReport {
|
|
17
|
+
readonly packages: readonly PackageDownloads[];
|
|
18
|
+
readonly totalDownloads: number;
|
|
19
|
+
readonly period: string;
|
|
20
|
+
readonly fetchedAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Fetch weekly downloads for a single npm package. */
|
|
24
|
+
async function fetchDownloads(name: string): Promise<PackageDownloads> {
|
|
25
|
+
const period = 'last-week';
|
|
26
|
+
try {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const timer = setTimeout(() => { controller.abort(); }, 10000);
|
|
29
|
+
const url = `https://api.npmjs.org/downloads/point/${period}/${encodeURIComponent(name)}`;
|
|
30
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
|
|
33
|
+
if (!resp.ok) {
|
|
34
|
+
return { name, downloads: 0, period, error: `HTTP ${resp.status}` };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = (await resp.json()) as { downloads: number };
|
|
38
|
+
return { name, downloads: data.downloads, period };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return { name, downloads: 0, period, error: err instanceof Error ? err.message : String(err) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Fetch downloads for all given package names. */
|
|
45
|
+
export async function fetchAllDownloads(packageNames: string[]): Promise<DownloadsReport> {
|
|
46
|
+
const results = await Promise.all(packageNames.map(fetchDownloads));
|
|
47
|
+
results.sort((a, b) => b.downloads - a.downloads);
|
|
48
|
+
return {
|
|
49
|
+
packages: results,
|
|
50
|
+
totalDownloads: results.reduce((sum, r) => sum + r.downloads, 0),
|
|
51
|
+
period: 'last-week',
|
|
52
|
+
fetchedAt: new Date().toISOString(),
|
|
53
|
+
};
|
|
54
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,10 @@ export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from '.
|
|
|
19
19
|
export { benchmarkSkill, benchmarkSkills, compareSkills } from './benchmark.js';
|
|
20
20
|
export { buildRegistry, searchRegistry, filterByCategory } from './registry.js';
|
|
21
21
|
export { discoverPackages, publishPackages } from './publish.js';
|
|
22
|
+
export { fetchAllDownloads } from './downloads.js';
|
|
23
|
+
export type { PackageDownloads, DownloadsReport } from './downloads.js';
|
|
24
|
+
export { discoverInstalled, checkUpgrades } from './upgrade.js';
|
|
25
|
+
export type { InstalledSkill, UpgradeCheck, UpgradeReport } from './upgrade.js';
|
|
22
26
|
export type { PublishResult, PublishReport } from './publish.js';
|
|
23
27
|
export type { RegistryEntry, Registry } from './registry.js';
|
|
24
28
|
export type { BenchmarkCheck, BenchmarkScore, BenchmarkReport, CompareResult } from './benchmark.js';
|
package/src/upgrade.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill upgrade — detects installed skills and re-applies from canonical source.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
/** Info about an installed skill. */
|
|
11
|
+
export interface InstalledSkill {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly size: number;
|
|
15
|
+
readonly modifiedAt: string;
|
|
16
|
+
readonly hasFrontmatter: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Upgrade check for a single skill. */
|
|
20
|
+
export interface UpgradeCheck {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly installed: InstalledSkill;
|
|
23
|
+
readonly canonicalSize: number | undefined;
|
|
24
|
+
readonly needsUpdate: boolean;
|
|
25
|
+
readonly reason: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Full upgrade report. */
|
|
29
|
+
export interface UpgradeReport {
|
|
30
|
+
readonly targetDir: string;
|
|
31
|
+
readonly installed: number;
|
|
32
|
+
readonly needsUpdate: number;
|
|
33
|
+
readonly upToDate: number;
|
|
34
|
+
readonly notInCanonical: number;
|
|
35
|
+
readonly skills: readonly UpgradeCheck[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Discover installed skills in a target platform directory. */
|
|
39
|
+
export function discoverInstalled(targetDir: string): InstalledSkill[] {
|
|
40
|
+
if (!existsSync(targetDir)) return [];
|
|
41
|
+
return readdirSync(targetDir, { withFileTypes: true })
|
|
42
|
+
.filter((e) => e.isDirectory() && existsSync(join(targetDir, e.name, 'SKILL.md')))
|
|
43
|
+
.map((e) => {
|
|
44
|
+
const skillMdPath = join(targetDir, e.name, 'SKILL.md');
|
|
45
|
+
const stat = statSync(skillMdPath);
|
|
46
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
47
|
+
return {
|
|
48
|
+
id: e.name,
|
|
49
|
+
path: join(targetDir, e.name),
|
|
50
|
+
size: stat.size,
|
|
51
|
+
modifiedAt: stat.mtime.toISOString().slice(0, 10),
|
|
52
|
+
hasFrontmatter: content.startsWith('---'),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Check which installed skills need updates by comparing with canonical sources. */
|
|
58
|
+
export function checkUpgrades(
|
|
59
|
+
targetDir: string,
|
|
60
|
+
canonicalDirs: string[],
|
|
61
|
+
): UpgradeReport {
|
|
62
|
+
const installed = discoverInstalled(targetDir);
|
|
63
|
+
|
|
64
|
+
// Build canonical index: skill id → { dir, size }
|
|
65
|
+
const canonical = new Map<string, { dir: string; size: number }>();
|
|
66
|
+
for (const cDir of canonicalDirs) {
|
|
67
|
+
if (!existsSync(cDir)) continue;
|
|
68
|
+
const skills = readdirSync(cDir, { withFileTypes: true })
|
|
69
|
+
.filter((e) => e.isDirectory() && existsSync(join(cDir, e.name, 'SKILL.md')));
|
|
70
|
+
for (const skill of skills) {
|
|
71
|
+
const skillMdPath = join(cDir, skill.name, 'SKILL.md');
|
|
72
|
+
const stat = statSync(skillMdPath);
|
|
73
|
+
canonical.set(skill.name, { dir: join(cDir, skill.name), size: stat.size });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const checks: UpgradeCheck[] = [];
|
|
78
|
+
|
|
79
|
+
for (const inst of installed) {
|
|
80
|
+
const can = canonical.get(inst.id);
|
|
81
|
+
if (!can) {
|
|
82
|
+
checks.push({
|
|
83
|
+
id: inst.id,
|
|
84
|
+
installed: inst,
|
|
85
|
+
canonicalSize: undefined,
|
|
86
|
+
needsUpdate: false,
|
|
87
|
+
reason: 'not in canonical (custom or removed)',
|
|
88
|
+
});
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const sizeDiff = Math.abs(inst.size - can.size);
|
|
93
|
+
const needsUpdate = sizeDiff > 0;
|
|
94
|
+
checks.push({
|
|
95
|
+
id: inst.id,
|
|
96
|
+
installed: inst,
|
|
97
|
+
canonicalSize: can.size,
|
|
98
|
+
needsUpdate,
|
|
99
|
+
reason: needsUpdate
|
|
100
|
+
? `size differs (installed: ${inst.size}B, canonical: ${can.size}B, delta: ${sizeDiff}B)`
|
|
101
|
+
: 'up to date',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
targetDir,
|
|
107
|
+
installed: installed.length,
|
|
108
|
+
needsUpdate: checks.filter((c) => c.needsUpdate).length,
|
|
109
|
+
upToDate: checks.filter((c) => !c.needsUpdate && c.canonicalSize !== undefined).length,
|
|
110
|
+
notInCanonical: checks.filter((c) => c.canonicalSize === undefined).length,
|
|
111
|
+
skills: checks,
|
|
112
|
+
};
|
|
113
|
+
}
|