@dzhechkov/harness-core 0.3.1 → 0.3.3

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
@@ -12,4 +12,6 @@ export * from './operations.js';
12
12
  export * from './workflows.js';
13
13
  export { createSkill } from './create-skill.js';
14
14
  export type { CreateSkillOptions, CreateSkillResult } from './create-skill.js';
15
+ export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
16
+ export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from './sync-upstream.js';
15
17
  //# 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"}
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"}
package/dist/index.js CHANGED
@@ -11,4 +11,5 @@ export * from './targets.js';
11
11
  export * from './operations.js';
12
12
  export * from './workflows.js';
13
13
  export { createSkill } from './create-skill.js';
14
+ export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
14
15
  //# 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"}
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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Upstream sync — fetches SKILL.md from origin repos defined in sources.json
3
+ * and reports which skills have upstream changes.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ /** A single skill's source mapping. */
8
+ export interface SkillSource {
9
+ readonly path: string;
10
+ readonly version?: string | undefined;
11
+ readonly trust?: string | undefined;
12
+ readonly note?: string | undefined;
13
+ }
14
+ /** The origin repo metadata. */
15
+ export interface OriginMeta {
16
+ readonly repo: string;
17
+ readonly url: string;
18
+ readonly branch: string;
19
+ readonly license: string;
20
+ readonly author: string;
21
+ readonly canonicalized: string;
22
+ }
23
+ /** The sources.json schema. */
24
+ export interface SourcesManifest {
25
+ readonly origin: OriginMeta;
26
+ readonly skills: Record<string, SkillSource>;
27
+ }
28
+ /** Result of checking one skill against upstream. */
29
+ export interface UpstreamCheckResult {
30
+ readonly skillId: string;
31
+ readonly originPath: string;
32
+ readonly status: 'up-to-date' | 'changed' | 'fetch-error' | 'local-missing';
33
+ readonly localLines?: number | undefined;
34
+ readonly upstreamLines?: number | undefined;
35
+ readonly error?: string | undefined;
36
+ }
37
+ /** Result of a full upstream sync check. */
38
+ export interface SyncUpstreamReport {
39
+ readonly origin: OriginMeta;
40
+ readonly checked: number;
41
+ readonly changed: number;
42
+ readonly upToDate: number;
43
+ readonly errors: number;
44
+ readonly skills: readonly UpstreamCheckResult[];
45
+ }
46
+ /** Load sources.json from a skills package directory. */
47
+ export declare function loadSourcesManifest(packageDir: string): SourcesManifest | undefined;
48
+ /**
49
+ * Check all skills in a sources.json manifest against their upstream.
50
+ * Compares line counts as a quick diff indicator (full diff would be too verbose).
51
+ */
52
+ export declare function checkUpstream(packageDir: string): Promise<SyncUpstreamReport | undefined>;
53
+ //# sourceMappingURL=sync-upstream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-upstream.d.ts","sourceRoot":"","sources":["../src/sync-upstream.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,uCAAuC;AACvC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CAC9C;AAED,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,GAAG,eAAe,CAAC;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAKnF;AA6BD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAiD/F"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Upstream sync — fetches SKILL.md from origin repos defined in sources.json
3
+ * and reports which skills have upstream changes.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ /** Load sources.json from a skills package directory. */
10
+ export function loadSourcesManifest(packageDir) {
11
+ const sourcesPath = join(packageDir, 'sources.json');
12
+ if (!existsSync(sourcesPath))
13
+ return undefined;
14
+ const raw = readFileSync(sourcesPath, 'utf-8');
15
+ return JSON.parse(raw);
16
+ }
17
+ /** Build the raw GitHub URL for a skill's SKILL.md. */
18
+ function rawUrl(origin, skillPath) {
19
+ return `https://raw.githubusercontent.com/${origin.repo}/${origin.branch}/${skillPath}`;
20
+ }
21
+ /** Fetch text content from a URL with timeout. */
22
+ async function fetchText(url, timeoutMs = 10000) {
23
+ try {
24
+ const controller = new AbortController();
25
+ const timer = setTimeout(() => { controller.abort(); }, timeoutMs);
26
+ const response = await fetch(url, { signal: controller.signal });
27
+ clearTimeout(timer);
28
+ if (!response.ok) {
29
+ return { ok: false, text: '', error: `HTTP ${response.status}` };
30
+ }
31
+ const text = await response.text();
32
+ return { ok: true, text };
33
+ }
34
+ catch (err) {
35
+ return { ok: false, text: '', error: err instanceof Error ? err.message : String(err) };
36
+ }
37
+ }
38
+ /** Count non-empty lines in text. */
39
+ function lineCount(text) {
40
+ return text.split('\n').length;
41
+ }
42
+ /**
43
+ * Check all skills in a sources.json manifest against their upstream.
44
+ * Compares line counts as a quick diff indicator (full diff would be too verbose).
45
+ */
46
+ export async function checkUpstream(packageDir) {
47
+ const manifest = loadSourcesManifest(packageDir);
48
+ if (!manifest)
49
+ return undefined;
50
+ const results = [];
51
+ for (const [skillId, source] of Object.entries(manifest.skills)) {
52
+ const localSkillMd = join(packageDir, skillId, 'SKILL.md');
53
+ if (!existsSync(localSkillMd)) {
54
+ results.push({ skillId, originPath: source.path, status: 'local-missing' });
55
+ continue;
56
+ }
57
+ const localText = readFileSync(localSkillMd, 'utf-8');
58
+ const url = rawUrl(manifest.origin, source.path);
59
+ const upstream = await fetchText(url);
60
+ if (!upstream.ok) {
61
+ results.push({
62
+ skillId,
63
+ originPath: source.path,
64
+ status: 'fetch-error',
65
+ error: upstream.error,
66
+ });
67
+ continue;
68
+ }
69
+ const localLines = lineCount(localText);
70
+ const upstreamLines = lineCount(upstream.text);
71
+ const changed = localLines !== upstreamLines;
72
+ results.push({
73
+ skillId,
74
+ originPath: source.path,
75
+ status: changed ? 'changed' : 'up-to-date',
76
+ localLines,
77
+ upstreamLines,
78
+ });
79
+ }
80
+ return {
81
+ origin: manifest.origin,
82
+ checked: results.length,
83
+ changed: results.filter((r) => r.status === 'changed').length,
84
+ upToDate: results.filter((r) => r.status === 'up-to-date').length,
85
+ errors: results.filter((r) => r.status === 'fetch-error' || r.status === 'local-missing').length,
86
+ skills: results,
87
+ };
88
+ }
89
+ //# sourceMappingURL=sync-upstream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-upstream.js","sourceRoot":"","sources":["../src/sync-upstream.ts"],"names":[],"mappings":"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"}
package/dist/targets.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare const TARGETS: {
13
13
  readonly codex: Adapter;
14
14
  readonly opencode: Adapter;
15
15
  readonly hermes: Adapter;
16
+ readonly openclaude: Adapter;
16
17
  };
17
18
  /** A valid `--target` name. */
18
19
  export type TargetName = keyof typeof TARGETS;
@@ -1 +1 @@
1
- {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;CAKwB,CAAC;AAE7C,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAE9C,uCAAuC;AACvC,eAAO,MAAM,YAAY,EAA2B,UAAU,EAAE,CAAC;AAEjE,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D"}
1
+ {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;;CAMwB,CAAC;AAE7C,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAE9C,uCAAuC;AACvC,eAAO,MAAM,YAAY,EAA2B,UAAU,EAAE,CAAC;AAEjE,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D"}
package/dist/targets.js CHANGED
@@ -7,6 +7,7 @@ import { claudeAdapter } from '@dzhechkov/adapter-claude';
7
7
  import { codexAdapter } from '@dzhechkov/adapter-codex';
8
8
  import { hermesAdapter } from '@dzhechkov/adapter-hermes';
9
9
  import { opencodeAdapter } from '@dzhechkov/adapter-opencode';
10
+ import { openclaudeAdapter } from '@dzhechkov/adapter-openclaude';
10
11
  /**
11
12
  * The targets the harness can initialise. The key is the CLI `--target` name
12
13
  * (`claude-code`, not `claude`); the value is the adapter that emits for it.
@@ -16,6 +17,7 @@ export const TARGETS = {
16
17
  codex: codexAdapter,
17
18
  opencode: opencodeAdapter,
18
19
  hermes: hermesAdapter,
20
+ openclaude: openclaudeAdapter,
19
21
  };
20
22
  /** Every supported `--target` name. */
21
23
  export const TARGET_NAMES = Object.keys(TARGETS);
@@ -1 +1 @@
1
- {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,aAAa,EAAE,aAAa;IAC5B,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;CACqB,CAAC;AAK7C,uCAAuC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAiB,CAAC;AAEjE,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,aAAa,EAAE,aAAa;IAC5B,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;IACrB,UAAU,EAAE,iBAAiB;CACa,CAAC;AAK7C,uCAAuC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAiB,CAAC;AAEjE,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC9D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/harness-core",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
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,11 +25,12 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "yaml": "^2.0.0",
28
+ "@dzhechkov/adapter-claude": "0.2.0",
29
+ "@dzhechkov/adapter-openclaude": "0.1.0",
28
30
  "@dzhechkov/adapter-codex": "0.2.0",
29
31
  "@dzhechkov/adapter-opencode": "0.2.0",
30
- "@dzhechkov/core": "0.2.0",
31
- "@dzhechkov/adapter-claude": "0.2.0",
32
- "@dzhechkov/adapter-hermes": "0.2.0"
32
+ "@dzhechkov/adapter-hermes": "0.2.0",
33
+ "@dzhechkov/core": "0.2.0"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/node": "^25.6.0",
package/src/index.ts CHANGED
@@ -14,3 +14,5 @@ export * from './operations.js';
14
14
  export * from './workflows.js';
15
15
  export { createSkill } from './create-skill.js';
16
16
  export type { CreateSkillOptions, CreateSkillResult } from './create-skill.js';
17
+ export { checkUpstream, loadSourcesManifest } from './sync-upstream.js';
18
+ export type { SyncUpstreamReport, UpstreamCheckResult, SourcesManifest } from './sync-upstream.js';
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Upstream sync — fetches SKILL.md from origin repos defined in sources.json
3
+ * and reports which skills have upstream changes.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+
8
+ import { existsSync, readFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ /** A single skill's source mapping. */
12
+ export interface SkillSource {
13
+ readonly path: string;
14
+ readonly version?: string | undefined;
15
+ readonly trust?: string | undefined;
16
+ readonly note?: string | undefined;
17
+ }
18
+
19
+ /** The origin repo metadata. */
20
+ export interface OriginMeta {
21
+ readonly repo: string;
22
+ readonly url: string;
23
+ readonly branch: string;
24
+ readonly license: string;
25
+ readonly author: string;
26
+ readonly canonicalized: string;
27
+ }
28
+
29
+ /** The sources.json schema. */
30
+ export interface SourcesManifest {
31
+ readonly origin: OriginMeta;
32
+ readonly skills: Record<string, SkillSource>;
33
+ }
34
+
35
+ /** Result of checking one skill against upstream. */
36
+ export interface UpstreamCheckResult {
37
+ readonly skillId: string;
38
+ readonly originPath: string;
39
+ readonly status: 'up-to-date' | 'changed' | 'fetch-error' | 'local-missing';
40
+ readonly localLines?: number | undefined;
41
+ readonly upstreamLines?: number | undefined;
42
+ readonly error?: string | undefined;
43
+ }
44
+
45
+ /** Result of a full upstream sync check. */
46
+ export interface SyncUpstreamReport {
47
+ readonly origin: OriginMeta;
48
+ readonly checked: number;
49
+ readonly changed: number;
50
+ readonly upToDate: number;
51
+ readonly errors: number;
52
+ readonly skills: readonly UpstreamCheckResult[];
53
+ }
54
+
55
+ /** Load sources.json from a skills package directory. */
56
+ export function loadSourcesManifest(packageDir: string): SourcesManifest | undefined {
57
+ const sourcesPath = join(packageDir, 'sources.json');
58
+ if (!existsSync(sourcesPath)) return undefined;
59
+ const raw = readFileSync(sourcesPath, 'utf-8');
60
+ return JSON.parse(raw) as SourcesManifest;
61
+ }
62
+
63
+ /** Build the raw GitHub URL for a skill's SKILL.md. */
64
+ function rawUrl(origin: OriginMeta, skillPath: string): string {
65
+ return `https://raw.githubusercontent.com/${origin.repo}/${origin.branch}/${skillPath}`;
66
+ }
67
+
68
+ /** Fetch text content from a URL with timeout. */
69
+ async function fetchText(url: string, timeoutMs: number = 10000): Promise<{ ok: boolean; text: string; error?: string }> {
70
+ try {
71
+ const controller = new AbortController();
72
+ const timer = setTimeout(() => { controller.abort(); }, timeoutMs);
73
+ const response = await fetch(url, { signal: controller.signal });
74
+ clearTimeout(timer);
75
+ if (!response.ok) {
76
+ return { ok: false, text: '', error: `HTTP ${response.status}` };
77
+ }
78
+ const text = await response.text();
79
+ return { ok: true, text };
80
+ } catch (err) {
81
+ return { ok: false, text: '', error: err instanceof Error ? err.message : String(err) };
82
+ }
83
+ }
84
+
85
+ /** Count non-empty lines in text. */
86
+ function lineCount(text: string): number {
87
+ return text.split('\n').length;
88
+ }
89
+
90
+ /**
91
+ * Check all skills in a sources.json manifest against their upstream.
92
+ * Compares line counts as a quick diff indicator (full diff would be too verbose).
93
+ */
94
+ export async function checkUpstream(packageDir: string): Promise<SyncUpstreamReport | undefined> {
95
+ const manifest = loadSourcesManifest(packageDir);
96
+ if (!manifest) return undefined;
97
+
98
+ const results: UpstreamCheckResult[] = [];
99
+
100
+ for (const [skillId, source] of Object.entries(manifest.skills)) {
101
+ const localSkillMd = join(packageDir, skillId, 'SKILL.md');
102
+
103
+ if (!existsSync(localSkillMd)) {
104
+ results.push({ skillId, originPath: source.path, status: 'local-missing' });
105
+ continue;
106
+ }
107
+
108
+ const localText = readFileSync(localSkillMd, 'utf-8');
109
+ const url = rawUrl(manifest.origin, source.path);
110
+ const upstream = await fetchText(url);
111
+
112
+ if (!upstream.ok) {
113
+ results.push({
114
+ skillId,
115
+ originPath: source.path,
116
+ status: 'fetch-error',
117
+ error: upstream.error,
118
+ });
119
+ continue;
120
+ }
121
+
122
+ const localLines = lineCount(localText);
123
+ const upstreamLines = lineCount(upstream.text);
124
+ const changed = localLines !== upstreamLines;
125
+
126
+ results.push({
127
+ skillId,
128
+ originPath: source.path,
129
+ status: changed ? 'changed' : 'up-to-date',
130
+ localLines,
131
+ upstreamLines,
132
+ });
133
+ }
134
+
135
+ return {
136
+ origin: manifest.origin,
137
+ checked: results.length,
138
+ changed: results.filter((r) => r.status === 'changed').length,
139
+ upToDate: results.filter((r) => r.status === 'up-to-date').length,
140
+ errors: results.filter((r) => r.status === 'fetch-error' || r.status === 'local-missing').length,
141
+ skills: results,
142
+ };
143
+ }
package/src/targets.ts CHANGED
@@ -8,6 +8,7 @@ import { claudeAdapter } from '@dzhechkov/adapter-claude';
8
8
  import { codexAdapter } from '@dzhechkov/adapter-codex';
9
9
  import { hermesAdapter } from '@dzhechkov/adapter-hermes';
10
10
  import { opencodeAdapter } from '@dzhechkov/adapter-opencode';
11
+ import { openclaudeAdapter } from '@dzhechkov/adapter-openclaude';
11
12
  import type { Adapter } from '@dzhechkov/core';
12
13
 
13
14
  /**
@@ -19,6 +20,7 @@ export const TARGETS = {
19
20
  codex: codexAdapter,
20
21
  opencode: opencodeAdapter,
21
22
  hermes: hermesAdapter,
23
+ openclaude: openclaudeAdapter,
22
24
  } as const satisfies Record<string, Adapter>;
23
25
 
24
26
  /** A valid `--target` name. */