@hasna/skills 0.1.70 → 0.1.71
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/bin/index.js +813 -70
- package/bin/mcp.js +1 -1
- package/bin/server.js +1 -1
- package/dist/cli/commands/hydrate.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +625 -1
- package/dist/lib/portable-snapshot-filter.d.ts +48 -0
- package/dist/lib/station-hydrate.d.ts +103 -0
- package/dist/lib/station-snapshot.d.ts +96 -0
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SyncAgent } from "./agent-sync.js";
|
|
2
|
+
/** A snapshot source: one directory of installed skills. */
|
|
3
|
+
export interface SyncHomeDefinition {
|
|
4
|
+
/** Directory name used in the snapshot destination and reporting. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Snapshot category: the standalone stores, or one of the agent homes. */
|
|
7
|
+
subClass: "skills" | "custom" | "agent-homes";
|
|
8
|
+
/** The coding agent whose home this is, when subClass is agent-homes. */
|
|
9
|
+
agent: SyncAgent | null;
|
|
10
|
+
}
|
|
11
|
+
/** The seven installed skill homes the per-station snapshot covers. */
|
|
12
|
+
export declare const SYNC_HOMES: readonly SyncHomeDefinition[];
|
|
13
|
+
/**
|
|
14
|
+
* Scanner-flagged portable files the fleet conformance gate refuses. They are
|
|
15
|
+
* absent from the snapshots by construction; the check stays as defense in
|
|
16
|
+
* depth. See the header comment for the provenance and retirement path.
|
|
17
|
+
*/
|
|
18
|
+
export declare const REFUSED_SCANNER_FLAGGED: Set<string>;
|
|
19
|
+
export declare function isExcludedSkillFileName(fileName: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The portable filter: only files at `<ident>/SKILL.md`, `<ident>/skill.json`,
|
|
22
|
+
* and `<ident>/scripts|assets|references/*` may reach a snapshot or the cache.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isPortableWithinSkill(relativeParts: string[]): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The on-disk location of one of the seven snapshot homes. `homesRoot` stages
|
|
27
|
+
* a copy of the homes (e.g. an rsync'd mirror of a remote station); when
|
|
28
|
+
* absent, this machine's real `$HOME` is the root. The staged layout mirrors
|
|
29
|
+
* the home mapping: `<dir>/skills`, `<dir>/<agent>/skills`,
|
|
30
|
+
* `<dir>/opencode/skills`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function homePathFor(definition: SyncHomeDefinition, homesRoot?: string): string;
|
|
33
|
+
/** Snapshot destination inside the repo, relative to the repo root. */
|
|
34
|
+
export declare function destinationFor(definition: SyncHomeDefinition, stationId: string, relativePath: string): string;
|
|
35
|
+
export interface WalkEntry {
|
|
36
|
+
kind: "file" | "symlink";
|
|
37
|
+
relativePath: string;
|
|
38
|
+
fullPath: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Recursive walk that records symlinks instead of following them, and skips
|
|
42
|
+
* the excluded directory names/patterns. A missing root reads as empty — the
|
|
43
|
+
* same contract as the source script, where an absent home is a zero-file
|
|
44
|
+
* home, not an error.
|
|
45
|
+
*/
|
|
46
|
+
export declare function walkEntries(absoluteRoot: string): WalkEntry[];
|
|
47
|
+
/** True when a path exists and is a regular file. */
|
|
48
|
+
export declare function isRegularFile(filePath: string): boolean;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type SyncAgent } from "./agent-sync.js";
|
|
2
|
+
/**
|
|
3
|
+
* The wire schema of the per-station hydration manifest; keeps the
|
|
4
|
+
* fleet-resources spelling for the same wire-compat reason as the
|
|
5
|
+
* sync-manifest.
|
|
6
|
+
*/
|
|
7
|
+
export declare const STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
8
|
+
/** Package identity recorded in the manifest's producer field. */
|
|
9
|
+
export declare const STATION_HYDRATION_PRODUCER: {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
};
|
|
13
|
+
export interface HydrationCandidate {
|
|
14
|
+
ident: string;
|
|
15
|
+
agent: SyncAgent;
|
|
16
|
+
/** Ident-relative portable path, e.g. `scripts/run.sh`. */
|
|
17
|
+
withinIdent: string;
|
|
18
|
+
fullPath: string;
|
|
19
|
+
size: number;
|
|
20
|
+
mtimeMs: number;
|
|
21
|
+
/** The manifest's recorded sha256 for (agent, home-relative path), if any. */
|
|
22
|
+
manifestHash: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface HydrationWinnerFile {
|
|
25
|
+
withinIdent: string;
|
|
26
|
+
winner: HydrationCandidate;
|
|
27
|
+
/** The losing copies' agents, for reporting. */
|
|
28
|
+
alternates: SyncAgent[];
|
|
29
|
+
}
|
|
30
|
+
export interface HydrationWinnerSkill {
|
|
31
|
+
ident: string;
|
|
32
|
+
files: HydrationWinnerFile[];
|
|
33
|
+
}
|
|
34
|
+
export interface StationHydrationOptions {
|
|
35
|
+
stationId: string;
|
|
36
|
+
/** Repo root holding resources/<stationId>/skills and its sync-manifest. */
|
|
37
|
+
repoRoot?: string;
|
|
38
|
+
/** Destination corpus cache; defaults to the canonical corpus root. */
|
|
39
|
+
cacheRoot?: string;
|
|
40
|
+
/** Report without writing anything (the default, as in the source script). */
|
|
41
|
+
dryRun?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface StationHydrationResult {
|
|
44
|
+
stationId: string;
|
|
45
|
+
mode: "dry-run" | "apply";
|
|
46
|
+
cacheRoot: string;
|
|
47
|
+
snapshotRoot: string;
|
|
48
|
+
sourceSnapshotSha: string;
|
|
49
|
+
stats: {
|
|
50
|
+
idents: number;
|
|
51
|
+
files: number;
|
|
52
|
+
bytes: number;
|
|
53
|
+
written?: number;
|
|
54
|
+
unchanged?: number;
|
|
55
|
+
};
|
|
56
|
+
/** The full winner plan, for reporting (never written to the manifest). */
|
|
57
|
+
winners: HydrationWinnerSkill[];
|
|
58
|
+
/** The manifest payload shape: per-skill files plus the skill's sha256. */
|
|
59
|
+
skills: Array<{
|
|
60
|
+
ident: string;
|
|
61
|
+
files: Array<{
|
|
62
|
+
relativePath: string;
|
|
63
|
+
sourceAgent: SyncAgent;
|
|
64
|
+
sourceMtimeMs: number;
|
|
65
|
+
size: number;
|
|
66
|
+
}>;
|
|
67
|
+
sha256: string | null;
|
|
68
|
+
}>;
|
|
69
|
+
manifestPath?: string;
|
|
70
|
+
}
|
|
71
|
+
interface SnapshotManifestFile {
|
|
72
|
+
relativePath: string;
|
|
73
|
+
agent: string | null;
|
|
74
|
+
sha256: string;
|
|
75
|
+
}
|
|
76
|
+
interface SnapshotManifest {
|
|
77
|
+
files?: SnapshotManifestFile[];
|
|
78
|
+
}
|
|
79
|
+
interface HydrationPlan {
|
|
80
|
+
manifest: SnapshotManifest;
|
|
81
|
+
sourceSnapshotSha: string;
|
|
82
|
+
winners: HydrationWinnerSkill[];
|
|
83
|
+
skippedByRule: Array<{
|
|
84
|
+
ident: string;
|
|
85
|
+
agent: SyncAgent;
|
|
86
|
+
relativePath: string;
|
|
87
|
+
reason: string;
|
|
88
|
+
}>;
|
|
89
|
+
totalFiles: number;
|
|
90
|
+
totalBytes: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build the dedup winner plan from the snapshot. Fails closed on symlinks and
|
|
94
|
+
* on a missing or unreadable sync-manifest.
|
|
95
|
+
*/
|
|
96
|
+
export declare function planStationHydration(stationId: string, repoRoot: string): HydrationPlan;
|
|
97
|
+
/**
|
|
98
|
+
* Run the hydration. Dry-run reports and writes nothing; apply detects
|
|
99
|
+
* conflicts across the whole plan first and only then writes, so the terminal
|
|
100
|
+
* refusal genuinely writes nothing.
|
|
101
|
+
*/
|
|
102
|
+
export declare function writeStationHydration(options: StationHydrationOptions): StationHydrationResult;
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type SyncHomeDefinition } from "./portable-snapshot-filter.js";
|
|
2
|
+
/**
|
|
3
|
+
* The wire schema of the per-station sync-manifest. The name keeps the
|
|
4
|
+
* fleet-resources spelling deliberately: the fleet hydrator (and the snapshot
|
|
5
|
+
* repo's consumers) record and compare this string, so the package-owned
|
|
6
|
+
* producer writes the same value the retired script wrote.
|
|
7
|
+
*/
|
|
8
|
+
export declare const STATION_SYNC_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-sync-manifest/v1";
|
|
9
|
+
/** Package identity recorded in the manifest's producer field. */
|
|
10
|
+
export declare const STATION_SNAPSHOT_PRODUCER: {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
};
|
|
14
|
+
export type StationSnapshotErrorCode = "INVALID_STATION" | "SYMLINKS_REFUSED" | "CONFLICT" | "DESTINATION_ESCAPE" | "MANIFEST_UNREADABLE";
|
|
15
|
+
export declare class StationSnapshotError extends Error {
|
|
16
|
+
readonly code: StationSnapshotErrorCode;
|
|
17
|
+
/** Per-item lines for the CONFLICT class (printed before the summary). */
|
|
18
|
+
readonly detail: string[];
|
|
19
|
+
constructor(code: StationSnapshotErrorCode, message: string, detail?: string[]);
|
|
20
|
+
}
|
|
21
|
+
/** Validate a station id the same way the source script does. */
|
|
22
|
+
export declare function validateStationId(stationId: string): void;
|
|
23
|
+
export interface PortableSnapshotFile {
|
|
24
|
+
relativePath: string;
|
|
25
|
+
fullPath: string;
|
|
26
|
+
size: number;
|
|
27
|
+
mtimeMs: number;
|
|
28
|
+
mtimeIso: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SnapshotSkip {
|
|
31
|
+
relativePath: string;
|
|
32
|
+
reason: "symlink" | "not-portable" | "excluded" | "refused-scanner-flagged" | "not-regular-file";
|
|
33
|
+
}
|
|
34
|
+
export interface ScannedHome {
|
|
35
|
+
definition: SyncHomeDefinition;
|
|
36
|
+
homePath: string;
|
|
37
|
+
portable: PortableSnapshotFile[];
|
|
38
|
+
skipped: SnapshotSkip[];
|
|
39
|
+
}
|
|
40
|
+
export interface SnapshotPlan {
|
|
41
|
+
definition: SyncHomeDefinition;
|
|
42
|
+
source: PortableSnapshotFile;
|
|
43
|
+
destination: string;
|
|
44
|
+
digest: string;
|
|
45
|
+
}
|
|
46
|
+
export interface StationSnapshotOptions {
|
|
47
|
+
stationId: string;
|
|
48
|
+
/** Target repo root; the snapshot lands in <repoRoot>/resources/<stationId>/skills. */
|
|
49
|
+
repoRoot?: string;
|
|
50
|
+
/** Stage the homes from a mirror instead of this machine's real $HOME. */
|
|
51
|
+
homesRoot?: string;
|
|
52
|
+
/** Report without writing anything (the default, as in the source script). */
|
|
53
|
+
dryRun?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface StationSnapshotManifestFile {
|
|
56
|
+
relativePath: string;
|
|
57
|
+
destination: string;
|
|
58
|
+
subClass: "skills" | "custom" | "agent-homes";
|
|
59
|
+
agent: string | null;
|
|
60
|
+
sha256: string;
|
|
61
|
+
sourceMtimeMs: number;
|
|
62
|
+
sourceMtimeIso: string;
|
|
63
|
+
size: number;
|
|
64
|
+
}
|
|
65
|
+
export interface StationSnapshotResult {
|
|
66
|
+
stationId: string;
|
|
67
|
+
mode: "dry-run" | "populate";
|
|
68
|
+
repoRoot: string;
|
|
69
|
+
stats: {
|
|
70
|
+
files: number;
|
|
71
|
+
bytes: number;
|
|
72
|
+
written?: number;
|
|
73
|
+
unchanged?: number;
|
|
74
|
+
};
|
|
75
|
+
homes: Array<{
|
|
76
|
+
name: string;
|
|
77
|
+
homePath: string;
|
|
78
|
+
files: number;
|
|
79
|
+
skipped: number;
|
|
80
|
+
}>;
|
|
81
|
+
manifestPath?: string;
|
|
82
|
+
files: StationSnapshotManifestFile[];
|
|
83
|
+
}
|
|
84
|
+
export declare function sha256File(filePath: string): string;
|
|
85
|
+
/** Scan every home and build the write plan; fails closed on symlinks. */
|
|
86
|
+
export declare function planStationSnapshot(options: StationSnapshotOptions): {
|
|
87
|
+
scanned: ScannedHome[];
|
|
88
|
+
plans: SnapshotPlan[];
|
|
89
|
+
totalBytes: number;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Run the per-station snapshot. Dry-run reports and writes nothing; populate
|
|
93
|
+
* detects conflicts across the whole plan first and only then writes, so the
|
|
94
|
+
* terminal refusal genuinely writes nothing.
|
|
95
|
+
*/
|
|
96
|
+
export declare function writeStationSnapshot(options: StationSnapshotOptions): StationSnapshotResult;
|