@hasna/skills 0.10.80 → 0.10.81

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.
@@ -93,6 +93,17 @@ export interface ReviewedDiscoveryInputs {
93
93
  /** Project one native configuration's witnessed fields, exactly as discovery
94
94
  * binds them. Used for both the current file and a preserved reviewed preimage. */
95
95
  export declare function projectNativeDiscoveryFields(text: string, format: "json" | "toml" | "yaml", fields: readonly string[], path: string): string;
96
+ /** Codex name reconcile only, after the caller verified that every enrolled
97
+ * plugin parent in `parents` kept its reviewed capability tuple (manifest apart
98
+ * from its version, app, MCP and remote receipt). A stale full-byte witness of a
99
+ * manifest, app or MCP declaration or skill document under one of those parents'
100
+ * version roots is replaced, in its own digest mode, by the same role's file in
101
+ * every current version root. Every other witness is kept unchanged, so any
102
+ * other change still refuses when the rebased binding is verified. */
103
+ export declare function rebaseCodexEnrolledCacheWitnesses(binding: AgentDiscoveryBinding, cache: string, parents: readonly string[], strictlySkillsOnly: (parent: string) => boolean): {
104
+ binding: AgentDiscoveryBinding;
105
+ rebased: string[];
106
+ };
96
107
  /** Called only after the owning planner validated discovery and capabilities.
97
108
  * New roots must already be body-free; absent roots need the prior typed proof. */
98
109
  export declare function captureRetiredCodexDiscovery(binding: AgentDiscoveryBinding, controls: CodexPluginSkillControl[], rules: unknown): AgentDiscoveryBinding;
@@ -1,5 +1,6 @@
1
1
  import { type CodexAdmissionReceipt, type CodexCorpusWriteOptions } from "./codex-corpus-write.js";
2
- import { type ReviewedCodexSkillDenial } from "./codex-plugin-skill-controls.js";
2
+ import { connectCodexHookRpc } from "./codex-hook-rpc.js";
3
+ import { type ReviewedCodexSkillDenial, type CodexPluginSkillControl } from "./codex-plugin-skill-controls.js";
3
4
  import { type CodexNativeHookEnvelope, type ProcessInspector, type NativePolicyHelperRunner } from "./codex-native-skill-policy.js";
4
5
  import { type CodexNativeSkillCatalog } from "./codex-native-skill-catalog.js";
5
6
  import type { NativeDiscoveryWitnessKind } from "./native-discovery-witness-change.js";
@@ -49,6 +50,7 @@ export interface AgentConfigChange {
49
50
  after: string;
50
51
  }
51
52
  export interface AgentIntegrationPlan {
53
+ codexReconcile?: CodexPluginReconcilePlanState;
52
54
  cacheWitnessUpgrade?: CodexSemanticCacheUpgrade;
53
55
  reviewArtifactSources?: Array<{
54
56
  path: string;
@@ -152,6 +154,7 @@ export declare function planAgentIntegration(options: {
152
154
  reviewedCacheAlias?: string;
153
155
  codexNativeCatalog?: CodexNativeSkillCatalog;
154
156
  codexSkillDenials?: ReviewedCodexSkillDenial[];
157
+ codexReconcile?: CodexPluginReconcileInput;
155
158
  }): AgentIntegrationPlan;
156
159
  export interface ClaudeManagedHookProjection {
157
160
  schema: "hasna.claude-managed-hook-projection.v1";
@@ -238,11 +241,126 @@ export interface ReviewArtifactMigration {
238
241
  }
239
242
  /** Explicit exact-proof migration. Never captures a new catalog or refreshes live inputs. */
240
243
  export declare function planReviewedArtifactMigration(options: ReviewArtifactMigration): AgentIntegrationPlan;
244
+ /** Exact copies, names and deny rows one Codex name reconcile plans. */
245
+ export interface CodexPluginReconcileInput {
246
+ copies: Array<{
247
+ path: string;
248
+ hash: string;
249
+ }>;
250
+ /** New qualified names added to the reviewed controls. */
251
+ names: string[];
252
+ /** Plugin parents whose documents the reconcile reviews: enrolled and newly enrolled. */
253
+ parents: string[];
254
+ /** Caller-verified native install receipts: of newly enrolled remote parents,
255
+ * and of enrolled remote parents whose stored controls lack one (healed). */
256
+ remoteIdentities: Record<string, string>;
257
+ /** Catalog-only skills outside the plugin cache: deny-only rows, no control. */
258
+ nameDenies: string[];
259
+ pathDenies: string[];
260
+ /** The saved review with stale enrolled-plugin cache witnesses re-captured. */
261
+ discovery?: AgentDiscoveryBinding;
262
+ }
263
+ export interface CodexPluginReconcileRequest {
264
+ home: string;
265
+ dataDir: string;
266
+ projectDir: string;
267
+ catalog: CodexNativeSkillCatalog;
268
+ }
269
+ export interface CodexPluginReconcilePlanState extends CodexPluginReconcileInput {
270
+ request: CodexPluginReconcileRequest;
271
+ }
272
+ export interface CodexPluginReconcileReport {
273
+ codexVersion: string;
274
+ /** Qualified names this reconcile adds to the reviewed controls and denies by name. */
275
+ enrolled: string[];
276
+ /** Qualified names already enrolled before this reconcile. */
277
+ alreadyEnrolled: number;
278
+ /** Catalog skills outside the plugin cache (Codex runtime plugins) denied by their stable qualified name. */
279
+ denied: string[];
280
+ /** Skills denied by exact path because no stable qualified name covers them, with the reason. */
281
+ pathDenied: Array<{
282
+ name: string;
283
+ path: string;
284
+ reason: "plugin-disabled" | "no-qualified-name";
285
+ }>;
286
+ /** Plugins explicitly disabled in [plugins]: no enrollment and no refusal. */
287
+ inactive: string[];
288
+ /** Every plugin this run classified as skills-only, citing the manifests read. */
289
+ classified: Array<{
290
+ pluginId: string;
291
+ capabilities: string[];
292
+ manifests: string[];
293
+ }>;
294
+ /** Reviewed cache witnesses re-captured after a skills-only version change. */
295
+ rebasedWitnesses: number;
296
+ /** Enrolled remote plugins whose controls stored without the install receipt gain the verified one. */
297
+ bound: string[];
298
+ /** Plugin-cache skill names left as they are: an existing path rule or an inactive plugin already makes them inert. */
299
+ skipped: number;
300
+ /** Native skills Codex loads after the reconcile; always exactly the bridge. */
301
+ allowed: string[];
302
+ }
303
+ /** A refusal: nothing was written and the guard keeps blocking with `code`. */
304
+ export declare class CodexPluginReconcileRefusal extends Error {
305
+ readonly code: string;
306
+ readonly refusals: Array<{
307
+ pluginId: string;
308
+ reasons: string[];
309
+ manifests?: string[];
310
+ }>;
311
+ constructor(code: string, detail: string, refusals?: Array<{
312
+ pluginId: string;
313
+ reasons: string[];
314
+ manifests?: string[];
315
+ }>);
316
+ }
317
+ /** The reconcile writes only appended disable rows and the bounded policy fields
318
+ * that record them; anything else is a different review and refuses. */
319
+ export declare function assertCodexReconcileScope(plan: AgentIntegrationPlan, scope: {
320
+ configPath: string;
321
+ policyPath: string;
322
+ prior: CodexPluginSkillControl[];
323
+ names: string[];
324
+ remoteIdentities?: Record<string, string>;
325
+ discovery: AgentDiscoveryBinding;
326
+ }): void;
327
+ /** Read-only plan for `skills hook codex-reconcile`. Enumerates the plugin-cache
328
+ * skills the strict guard refuses and the catalog skills Codex would load from
329
+ * its runtime cache; enrolls plugin-cache names whose capability tuple is
330
+ * unchanged (or a not yet enrolled skills-only plugin), denies runtime skills by
331
+ * qualified name (by exact path, reported, when Codex gives none), and refreshes
332
+ * the reviewed binding through planAgentIntegration. A null plan means nothing
333
+ * needs writing. Every refusal writes nothing. */
334
+ export declare function planCodexPluginNameReconcile(request: CodexPluginReconcileRequest): {
335
+ plan: AgentIntegrationPlan | null;
336
+ report: CodexPluginReconcileReport;
337
+ };
241
338
  export declare function applyAgentIntegration(plan: AgentIntegrationPlan, options?: CodexCorpusWriteOptions): {
242
339
  changed: string[];
243
340
  backups: string[];
244
341
  rootAliases?: AgentRootAlias[];
245
342
  } & CodexAdmissionReceipt;
343
+ export interface CodexReconcileApplyOptions extends CodexCorpusWriteOptions {
344
+ /** The Codex home the owned app-server uses; omit for the default ~/.codex. */
345
+ codexHome?: string;
346
+ /** Test seam for the owned Codex app-server; production starts `codex app-server`. */
347
+ connect?: typeof connectCodexHookRpc;
348
+ }
349
+ export interface CodexReconcileApplyResult extends CodexAdmissionReceipt {
350
+ changed: string[];
351
+ backups: string[];
352
+ receipt?: string;
353
+ nativeConfigVersion?: string;
354
+ rootAliases?: AgentRootAlias[];
355
+ }
356
+ /** Apply a Codex name reconcile. The deny rows go through Codex's own writer
357
+ * (`config/batchWrite` with the user layer's `expectedVersion` and
358
+ * `reloadUserConfig`), so a concurrent Codex writer such as the desktop app
359
+ * makes the write refuse instead of being overwritten. The policy is written in
360
+ * the Skills transaction only after the config readback matches the plan.
361
+ * Either failure is compensated; the config is restored only through Codex's
362
+ * writer and only when nobody changed it since this run's write. */
363
+ export declare function applyCodexPluginReconcile(plan: AgentIntegrationPlan, options?: CodexReconcileApplyOptions): Promise<CodexReconcileApplyResult>;
246
364
  export declare function archiveNativeSkills(inventory: NativeSkillEntry[], options: CodexCorpusWriteOptions & {
247
365
  dataDir?: string;
248
366
  includeUnmanaged?: boolean;
@@ -12,6 +12,29 @@ export interface CodexNativeSkillCatalog {
12
12
  skills: CodexNativeSkill[];
13
13
  /** Added in 0.10.20; older reviewed skills/list receipts remain valid for listed skills. */
14
14
  plugins?: CodexInstalledPlugin[];
15
+ /** Remote install receipts read before the capture started any Codex process. */
16
+ remoteReceipts?: CodexRemoteReceiptSnapshot;
17
+ }
18
+ /** Schema of the remote install receipt snapshot embedded in a captured catalog. */
19
+ export declare const CODEX_REMOTE_RECEIPT_SNAPSHOT_SCHEMA: "hasna.skills-codex-remote-receipts.v1";
20
+ /** One plugin cache parent's native remote install receipt as it was on disk
21
+ * before Codex started. `remotePluginId` is null when the receipt is not a
22
+ * strictly valid schema-1 receipt; `sha256` is null when it is not a regular,
23
+ * unlinked file of at most 16 KiB. */
24
+ export type CodexRemoteReceiptObservation = {
25
+ pluginId: string;
26
+ receipt: "absent";
27
+ } | {
28
+ pluginId: string;
29
+ receipt: "present";
30
+ sha256: string | null;
31
+ remotePluginId: string | null;
32
+ mtimeMs: number;
33
+ };
34
+ export interface CodexRemoteReceiptSnapshot {
35
+ schema: typeof CODEX_REMOTE_RECEIPT_SNAPSHOT_SCHEMA;
36
+ cacheRoot: string;
37
+ plugins: CodexRemoteReceiptObservation[];
15
38
  }
16
39
  /** Safe installed-plugin identity needed when Codex hides a path-disabled skill. */
17
40
  export interface CodexInstalledPlugin {
@@ -28,6 +51,11 @@ export interface CodexInstalledPlugin {
28
51
  /** Declared local installation input, distinct from the loaded cache root. */
29
52
  sourcePath?: string;
30
53
  }
54
+ /** The one remote rule for every receipt decision (install review, reconcile
55
+ * and the inactive rule): Codex reports a remote source, or reports any remote
56
+ * identity. A projected row can keep its remotePluginId while a missing or
57
+ * unrecognized source projects to no sourceType, so the id alone counts. */
58
+ export declare function codexPluginIsRemote(plugin: Pick<CodexInstalledPlugin, "sourceType" | "remotePluginId"> | undefined): boolean;
31
59
  /** Codex 0.160 derives remote installed keys from these native scope realms.
32
60
  * A nonremote declaration in one of them cannot prove config-owned enablement:
33
61
  * remote reconciliation may replace enabled even while config stays disabled. */
@@ -42,6 +70,16 @@ export declare function projectCodexNativeSkillCatalog(response: unknown, cwd: s
42
70
  export declare function projectCodexInstalledPlugins(response: unknown): CodexInstalledPlugin[];
43
71
  /** Validate the bounded stored projection when it is consumed by a review. */
44
72
  export declare function projectCodexInstalledPluginEntries(value: unknown): CodexInstalledPlugin[];
73
+ /** Read every plugin cache parent's native remote install receipt. Call it
74
+ * before any Codex process starts: starting the app-server rewrites each remote
75
+ * plugin's receipt to its own state, so a later read observes healed bytes and
76
+ * cannot show a receipt that was missing or stale. Never follows an alias. */
77
+ export declare function captureCodexRemoteReceiptSnapshot(cache: string): CodexRemoteReceiptSnapshot;
78
+ /** The plugin cache identity a snapshot is bound to: the real path of its
79
+ * deepest existing ancestor plus the rest, so an absent cache still compares. */
80
+ export declare function canonicalCacheRoot(cache: string): string;
81
+ /** Validate an embedded snapshot when a review consumes it; null when invalid. */
82
+ export declare function projectCodexRemoteReceiptSnapshot(value: unknown): CodexRemoteReceiptSnapshot | null;
45
83
  /** Read one consumer's catalog through an owned, bounded native client. The RPC
46
84
  * transport owns request deadlines and child shutdown. Native discovery can
47
85
  * maintain its own caches; callers must constrain writes when requiring isolation. */
@@ -19,6 +19,14 @@ export declare const CODEX_PLUGIN_MANIFEST_DIRECTORIES: readonly string[];
19
19
  * codexPluginRootCapabilityEntry so a case-insensitive file system cannot alias
20
20
  * one. */
21
21
  export declare const CODEX_PLUGIN_ROOT_CAPABILITY_ENTRIES: readonly string[];
22
+ /** The only Codex manifest keys that are identity and display metadata, the
23
+ * default skills root and the reviewed `.app.json` connector declaration. Any
24
+ * other key (hooks, commands, extensions, MCP servers, agents, unknown keys)
25
+ * is a capability or unreviewed. The vendor-refresh guard admits exactly these;
26
+ * the Codex name reconcile treats `apps` as the app capability. */
27
+ export declare const CODEX_PLUGIN_MANIFEST_KNOWN_KEYS: readonly string[];
28
+ /** The default skills root, which vendor manifests spell out. */
29
+ export declare const CODEX_PLUGIN_DEFAULT_SKILLS_PATHS: readonly string[];
22
30
  /** Case-insensitive comparison form. Upper- then lower-casing also folds the
23
31
  * Kelvin sign, long s and dotless i onto their ASCII letters, so a name a
24
32
  * case-insensitive file system could open as a capability entry folds onto it. */
@@ -0,0 +1,74 @@
1
+ import { type CodexReconcileApplyOptions } from "./agent-integration.js";
2
+ import { type CodexNativeSkillCatalog } from "./codex-native-skill-catalog.js";
3
+ export declare const CODEX_RECONCILE_RESULT_SCHEMA: "hasna.skills-codex-reconcile.v1";
4
+ export type CodexReconcileOutcome = "applied" | "unchanged" | "planned" | "refused" | "error";
5
+ export interface CodexReconcileResult {
6
+ schema: typeof CODEX_RECONCILE_RESULT_SCHEMA;
7
+ mode: "preview" | "apply";
8
+ outcome: CodexReconcileOutcome;
9
+ /** Present exactly when the outcome is refused or error. */
10
+ code?: string;
11
+ message?: string;
12
+ codexVersion: string | null;
13
+ counts: {
14
+ enrolled: number;
15
+ alreadyEnrolled: number;
16
+ denied: number;
17
+ pathDenied: number;
18
+ skipped: number;
19
+ allowed: number;
20
+ };
21
+ /** Plugin-cache names added to the reviewed controls and denied by name. */
22
+ enrolled: string[];
23
+ /** Runtime-cache skills denied by their stable qualified name (no plugin control exists for them). */
24
+ denied: string[];
25
+ /** Skills denied by exact path, each with why no stable qualified name covers it. */
26
+ pathDenied: Array<{
27
+ name: string;
28
+ path: string;
29
+ reason: "plugin-disabled" | "no-qualified-name";
30
+ }>;
31
+ /** Inactive plugins (config disables them and Codex reports them disabled from a config-controlled source): neither enrolled nor refused. */
32
+ inactive: string[];
33
+ /** Plugins classified skills-only, each citing the manifest paths that were read. */
34
+ classified: Array<{
35
+ pluginId: string;
36
+ capabilities: string[];
37
+ manifests: string[];
38
+ }>;
39
+ /** Reviewed cache witnesses re-captured after a skills-only version change. */
40
+ rebasedWitnesses: number;
41
+ /** Enrolled remote plugins whose controls, stored without the install receipt, gain the verified one. */
42
+ bound: string[];
43
+ /** Native skills Codex loads afterwards; exactly the bridge, or the result is refused. */
44
+ allowed: string[];
45
+ /** Plugins whose capability kinds need a review, with the manifests read (refused only). */
46
+ refusals?: Array<{
47
+ pluginId: string;
48
+ reasons: string[];
49
+ manifests?: string[];
50
+ }>;
51
+ /** Files written (applied) or that apply would write (planned). */
52
+ changes: string[];
53
+ receipt?: string;
54
+ backups?: string[];
55
+ }
56
+ export declare function classifyCodexReconcileError(error: unknown): Pick<CodexReconcileResult, "outcome" | "code" | "message" | "refusals">;
57
+ export interface CodexReconcileOptions {
58
+ apply: boolean;
59
+ home: string;
60
+ projectDir: string;
61
+ dataDir?: string;
62
+ codexCommand?: string;
63
+ codexHome?: string;
64
+ /** Test seam for the native catalog read; production uses the Codex app-server. */
65
+ captureCatalog?: (options: {
66
+ command: string;
67
+ home: string;
68
+ codexHome?: string;
69
+ cwd: string;
70
+ }) => Promise<CodexNativeSkillCatalog>;
71
+ /** Test seam for Codex's own config writer; production starts `codex app-server`. */
72
+ connect?: CodexReconcileApplyOptions["connect"];
73
+ }
74
+ export declare function runCodexPluginReconcile(options: CodexReconcileOptions): Promise<CodexReconcileResult>;
@@ -1,4 +1,4 @@
1
- import { type CodexNativeSkillCatalog } from "./codex-native-skill-catalog.js";
1
+ import { type CodexInstalledPlugin, type CodexNativeSkillCatalog } from "./codex-native-skill-catalog.js";
2
2
  export interface CodexPluginSkillControl {
3
3
  name: string;
4
4
  pluginId: string;
@@ -35,6 +35,35 @@ type Read = (path: string) => string;
35
35
  /** The remote plugin identifier from the exact native install receipt Codex
36
36
  * writes beside a remote plugin's versions, or null when it is absent or invalid. */
37
37
  export declare function codexRemoteInstallReceipt(parent: string, read: Read): string | null;
38
+ /** Named install refusals for a plugin Codex's catalog reports as remote. */
39
+ export declare const CODEX_REMOTE_RECEIPT_MISSING = "CODEX_REMOTE_RECEIPT_MISSING";
40
+ export declare const CODEX_REMOTE_RECEIPT_MISMATCH = "CODEX_REMOTE_RECEIPT_MISMATCH";
41
+ export declare const CODEX_REMOTE_RECEIPT_SNAPSHOT_REQUIRED = "CODEX_REMOTE_RECEIPT_SNAPSHOT_REQUIRED";
42
+ /** The remote identity the reconcile may bind for a plugin Codex reports as
43
+ * remote: the catalog's id when the receipt read before Codex started named it
44
+ * and the current receipt still does; null otherwise. A catalog without a
45
+ * valid pre-start reading for this cache refuses (SNAPSHOT_REQUIRED). */
46
+ export declare function codexPreStartRemoteReceipt(catalog: CodexNativeSkillCatalog, cache: string, parent: string, plugin: {
47
+ id: string;
48
+ remotePluginId?: string;
49
+ }, read: Read): string | null;
50
+ /** Whether an enrolled parent's bound receipt was not there, with that id,
51
+ * before Codex started. Applies when a control binds a receipt and the parent
52
+ * exists (an absent parent is left to its own rules); a catalog without a
53
+ * valid pre-start reading for this cache then refuses (SNAPSHOT_REQUIRED). */
54
+ export declare function codexPreStartReceiptChanged(catalog: CodexNativeSkillCatalog, cache: string, parentControls: CodexPluginSkillControl[]): boolean;
55
+ /** What a reconcile does with an enrolled parent whose stored controls do not
56
+ * all carry its receipt (policies stored before every control recorded it).
57
+ * A plugin Codex reports as remote is healed: every control gets the catalog's
58
+ * id, on exactly the fresh-install proof (requireRemoteInstallReceipt: the
59
+ * pre-start reading and the current receipt both name it; MISSING or MISMATCH
60
+ * otherwise), and only when every bound control already agrees ("refuse").
61
+ * A plugin Codex does not report as remote refuses when only some controls are
62
+ * bound. null: nothing to heal (every control bound, none bound and not
63
+ * reported remote, or the parent is absent and left to its own rules). */
64
+ export declare function codexStoredReceiptHeal(catalog: CodexNativeSkillCatalog, cache: string, parentControls: CodexPluginSkillControl[], plugin: CodexInstalledPlugin | undefined, read: Read): {
65
+ heal: string;
66
+ } | "refuse" | null;
38
67
  /** Whether a vendor refresh's `.app.json` connector declaration is valid under
39
68
  * the same appControls validation rootControls applies (duplicate keys, the
40
69
  * single `apps` object, app names, `id`/`category`/`required` only, types and
@@ -85,5 +114,43 @@ export declare function reviewCodexPluginSkillControls(catalog: CodexNativeSkill
85
114
  * The caller verifies discovery/config witnesses before consuming this result. */
86
115
  export declare function isReviewedCodexPluginInactive(document: string, cache: string, controls: CodexInactivePluginControl[], pluginSettings: unknown, rules: unknown, read: Read): boolean;
87
116
  export declare function isReviewedCodexPluginSkillDisabled(document: string, cache: string, controls: CodexPluginSkillControl[], rules: unknown, read: Read): boolean;
117
+ /** Refusal code for a plugin change that touches more than its skill names: a
118
+ * new or changed app, MCP, hooks, manifest or remote installation capability. */
119
+ export declare const CODEX_PLUGIN_CAPABILITY_REVIEW_REQUIRED = "CODEX_PLUGIN_CAPABILITY_REVIEW_REQUIRED";
120
+ export type CodexPluginCapabilityReason = "app" | "mcp" | "hooks" | "commands" | "agents" | "manifest" | "manifest-location" | "remote" | "unsupported";
121
+ /** Why a plugin Skills has not enrolled needs a review before its skills can be
122
+ * enrolled automatically. Empty only when every version root holds nothing but
123
+ * a supported skills-only manifest and skills. A remote installation is not
124
+ * itself a capability; its receipt is checked separately. */
125
+ export declare function unenrolledCodexPluginCapabilities(parent: string, read: Read): CodexPluginCapabilityReason[];
126
+ /** The same classification with its evidence: every manifest path read. A
127
+ * skills-only result (no reasons) always names at least one manifest. */
128
+ export declare function unenrolledCodexPluginEvidence(parent: string, read: Read): {
129
+ reasons: CodexPluginCapabilityReason[];
130
+ manifests: string[];
131
+ };
132
+ /** Capability evidence for a plugin root outside the plugin cache (a Codex
133
+ * runtime), where the version-directory contract does not apply. */
134
+ export declare function codexPluginRootEvidence(root: string, read: Read): {
135
+ reasons: CodexPluginCapabilityReason[];
136
+ manifests: string[];
137
+ };
138
+ /** The strict scan for an enrolled plugin, run in addition to the reviewed-tuple
139
+ * check over every current version root: anything the reviewed tuple does not
140
+ * bind (a root plugin.json, hooks.json, commands/ or agents/, another manifest
141
+ * directory, a case variant, a nested declaration, a link) is a capability. */
142
+ export declare function enrolledCodexPluginStrictChanges(parentControls: CodexPluginSkillControl[], read: Read): CodexPluginCapabilityReason[];
143
+ /** Which capability of an enrolled plugin parent no longer matches its reviewed
144
+ * controls. Empty exactly when reviewedCodexPluginCapabilitiesUnchanged accepts it. */
145
+ export declare function codexPluginCapabilityChanges(cache: string, parentControls: CodexPluginSkillControl[], read: Read, rules?: unknown): CodexPluginCapabilityReason[];
146
+ /** Add newly enrolled qualified names to the reviewed control set. Never removes
147
+ * an existing control, and changes one only to bind `remoteIdentities[parent]`,
148
+ * the caller-verified native install receipt, onto an enrolled control stored
149
+ * without it (codexStoredReceiptHeal). A name under an enrolled plugin parent
150
+ * takes that parent's reviewed identity, and the native review must report the
151
+ * same capability tuple for it; it never drops a receipt the review verified.
152
+ * A name under a plugin Skills has not enrolled is admitted only for a
153
+ * skills-only plugin, and binds `remoteIdentities[parent]` when it is remote. */
154
+ export declare function mergeReconciledCodexPluginControls(prior: CodexPluginSkillControl[], reviewed: CodexPluginSkillControl[], names: string[], remoteIdentities?: Record<string, string>): CodexPluginSkillControl[];
88
155
  export declare function disableReviewedCodexPluginNames(text: string, controls: CodexPluginSkillControl[]): string;
89
156
  export {};
@@ -11,6 +11,7 @@ import { type RemoteCreditCheckout, type RemoteCreditCheckoutOptions, type Remot
11
11
  import { type UpdateRemoteProfile, type UpdateRemoteWorkspace } from "./remote-profile.js";
12
12
  import { type RemoteQuoteUnavailableCode } from "./remote-quote-errors.js";
13
13
  import { type RemoteSkillsAccess } from "./remote-permissions.js";
14
+ import { type SkillMetadataPatch, type SkillMetadataUpdatePlan } from "./skill-metadata-update.js";
14
15
  export type { RemoteQuoteUnavailableCode } from "./remote-quote-errors.js";
15
16
  /**
16
17
  * A server that predates this client's pin/tag/incremental-sync routes answered
@@ -123,6 +124,15 @@ export declare class RemoteSkillsClient {
123
124
  listSkills(): Promise<any[]>;
124
125
  getSkillMd(slug: string): Promise<string | null>;
125
126
  getSkill(slug: string): Promise<any | null>;
127
+ private metadataOrganization;
128
+ private readMetadataSnapshot;
129
+ /** Read-only preview. Save this complete private original before applying it. */
130
+ prepareSkillMetadataUpdate(slug: string, patch: SkillMetadataPatch): Promise<SkillMetadataUpdatePlan>;
131
+ /** One guarded PATCH; an uncertain response is reconciled by readback, never retried. */
132
+ applySkillMetadataUpdate(input: SkillMetadataUpdatePlan): Promise<{
133
+ status: "applied" | "reconciled" | "unchanged";
134
+ revisionId: string;
135
+ }>;
126
136
  /**
127
137
  * Raw GET for one skill, with the HTTP status surfaced. Used by the reconcile
128
138
  * re-check (registry-reconcile.ts) so it can distinguish "no such skill" (404) from
@@ -0,0 +1,46 @@
1
+ import { type RevisionContent } from "./revision.js";
2
+ export interface SkillMetadataPatch {
3
+ category?: string;
4
+ tags?: string[];
5
+ }
6
+ /** Private preservation snapshot: includes the original instruction body. */
7
+ export interface SkillMetadataUpdatePlan {
8
+ schemaVersion: 1;
9
+ authority: string;
10
+ organizationId: string;
11
+ expectedRevisionId: string;
12
+ originalDocument: string;
13
+ before: RevisionContent;
14
+ patch: SkillMetadataPatch;
15
+ }
16
+ export declare class SkillMetadataUpdateError extends Error {
17
+ readonly code: "METADATA_AUTH_REQUIRED" | "INVALID_METADATA_PATCH" | "INVALID_METADATA_SNAPSHOT" | "INVALID_METADATA_PLAN" | "METADATA_REVISION_CONFLICT" | "METADATA_UPDATE_UNCONFIRMED";
18
+ constructor(code: "METADATA_AUTH_REQUIRED" | "INVALID_METADATA_PATCH" | "INVALID_METADATA_SNAPSHOT" | "INVALID_METADATA_PLAN" | "METADATA_REVISION_CONFLICT" | "METADATA_UPDATE_UNCONFIRMED");
19
+ }
20
+ /** Read category and tags once into a plain copy, then validate and return only that copy. */
21
+ export declare function metadataPatch(value: unknown): SkillMetadataPatch;
22
+ /** Normalize exactly the owning registry's revision fields, never arbitrary extras. */
23
+ export declare function metadataSnapshot(value: unknown, etag: string | null): {
24
+ content: RevisionContent;
25
+ revisionId: string;
26
+ };
27
+ export declare function metadataPlan(value: unknown): SkillMetadataUpdatePlan;
28
+ export declare function metadataPlanSummary(plan: SkillMetadataUpdatePlan): {
29
+ slug: string;
30
+ authority: string;
31
+ organizationId: string;
32
+ expectedRevisionId: string;
33
+ before: {
34
+ category: string;
35
+ tags: string[];
36
+ };
37
+ after: {
38
+ category: string;
39
+ tags: string[];
40
+ };
41
+ preserved: {
42
+ version: string | undefined;
43
+ bundleSha256: string | undefined;
44
+ instructionBody: string;
45
+ };
46
+ };
@@ -49,5 +49,7 @@ export interface SkillValidationProvenance {
49
49
  packageSkillSource?: string;
50
50
  }
51
51
  export declare function parseSkillFrontmatter(content: string): SkillFrontmatter | null;
52
- export declare function validateSkillDirectory(name: string, skillPath: string, registryMeta?: SkillMeta): SkillValidationResult;
52
+ export declare function validateSkillDirectory(name: string, skillPath: string, registryMeta?: SkillMeta, options?: {
53
+ kind?: SkillKind;
54
+ }): SkillValidationResult;
53
55
  export declare function validateRegistryConsistency(registry: SkillMeta[], skillsDir: string): RegistryConsistencyResult;
@@ -49,7 +49,7 @@ var __require = import.meta.require;
49
49
  // src/runtime-prerequisites.ts
50
50
  import { isAbsolute as isAbsolute7 } from "path";
51
51
  // package.json
52
- var version = "0.10.80";
52
+ var version = "0.10.81";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,
@@ -39,6 +39,7 @@ export * from "./spend.js";
39
39
  export * from "./offline.js";
40
40
  export * from "./execution/index.js";
41
41
  export { RemoteSkillsClient, createRemoteSkillsClient, RemoteRequestError, RemoteCreditCheckoutError, RemoteRouteUnsupportedError, RemoteCapabilityUnavailableError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError } from "../lib/remote-client.js";
42
+ export { SkillMetadataUpdateError, metadataPlanSummary, type SkillMetadataPatch, type SkillMetadataUpdatePlan } from "../lib/skill-metadata-update.js";
42
43
  export { RemoteSkillsPermissionError, type RemoteSkillsAccess, type RemoteSkillsPermissions } from "../lib/remote-permissions.js";
43
44
  export * from "../lib/remote-recurring-contract.js";
44
45
  export { RecurringInputError, RemoteRecurringError, RemoteRecurringUnavailableError, RemoteRecurringReadError, RemoteRecurringUnconfirmedError } from "../lib/remote-recurring.js";