@hasna/skills 0.10.54 → 0.10.55

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 CHANGED
@@ -36888,7 +36888,7 @@ var package_default;
36888
36888
  var init_package = __esm(() => {
36889
36889
  package_default = {
36890
36890
  name: "@hasna/skills",
36891
- version: "0.10.54",
36891
+ version: "0.10.55",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -86070,8 +86070,12 @@ function assertPlanWindow(issuedAt, expiresAt, now3) {
86070
86070
  throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
86071
86071
  }
86072
86072
  }
86073
+ function selectionMetadata(selection) {
86074
+ return { slug: selection.slug, version: selection.version, bundleDigest: selection.bundleDigest };
86075
+ }
86073
86076
  function inspectSkillSession(sessionId, options = {}) {
86074
86077
  const snapshot2 = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot2;
86078
+ const loaded = new Set(receipt.loaded);
86075
86079
  return {
86076
86080
  sessionId,
86077
86081
  path: snapshot2.path,
@@ -86083,7 +86087,8 @@ function inspectSkillSession(sessionId, options = {}) {
86083
86087
  profileId: receipt.profile.profileId,
86084
86088
  profileRevision: receipt.profile.profileRevision,
86085
86089
  selectionCount: receipt.profile.selections.length,
86086
- loadedCount: receipt.loaded.length
86090
+ loadedCount: receipt.loaded.length,
86091
+ selections: receipt.profile.selections.map((selection) => ({ ...selectionMetadata(selection), loaded: loaded.has(selectionKey(selection)) }))
86087
86092
  };
86088
86093
  }
86089
86094
  function renewalDeadlineSignal(error61) {
@@ -86305,6 +86310,21 @@ async function reconcileSkillSession(input2, options = {}) {
86305
86310
  }
86306
86311
  const targetKeys = new Set(target.selections.map(selectionKey));
86307
86312
  const loaded = old.loaded.filter((key) => targetKeys.has(key));
86313
+ const previouslyLoaded = new Set(old.loaded);
86314
+ const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
86315
+ const previousSlugs = new Set(old.profile.selections.map((selection) => selection.slug));
86316
+ const selectionDelta = old.profile.selections.map((previous) => {
86317
+ const current = targetBySlug.get(previous.slug);
86318
+ const wasLoaded = previouslyLoaded.has(selectionKey(previous));
86319
+ const reason2 = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
86320
+ return {
86321
+ selection: selectionMetadata(previous),
86322
+ loaded: wasLoaded,
86323
+ outcome: !wasLoaded ? "unloaded" : reason2 === "same-bundle" ? "retained" : "retired",
86324
+ reason: reason2,
86325
+ ...reason2 === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
86326
+ };
86327
+ });
86308
86328
  const replacementGeneration = nextSkillSessionGeneration(snapshot2.generation);
86309
86329
  const plan = {
86310
86330
  schemaVersion: 1,
@@ -86317,6 +86337,8 @@ async function reconcileSkillSession(input2, options = {}) {
86317
86337
  replacementGeneration,
86318
86338
  retainedLoadedCount: loaded.length,
86319
86339
  retiredLoadedCount: old.loaded.length - loaded.length,
86340
+ selectionDelta,
86341
+ addedSelections: target.selections.filter((selection) => !previousSlugs.has(selection.slug)).map(selectionMetadata),
86320
86342
  scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
86321
86343
  };
86322
86344
  const planDigest = digest3(plan);
@@ -86356,6 +86378,9 @@ __export(exports_context, {
86356
86378
  contextResolverOptions: () => contextResolverOptions
86357
86379
  });
86358
86380
  import { readSync as readSync12 } from "fs";
86381
+ function sessionSelectionLabel(selection) {
86382
+ return `${selection.slug}@${selection.version} ${selection.bundleDigest}`;
86383
+ }
86359
86384
  function selectedProfileId(explicit) {
86360
86385
  return explicit ?? process.env.HASNA_SKILLS_SELECTION_PROFILE ?? readManagedSkillPolicy()?.profileId ?? "default";
86361
86386
  }
@@ -86373,8 +86398,12 @@ function registerContextCommands(parent) {
86373
86398
  sessions.command("show <id>").option("--json", "Return metadata as JSON", false).description("Inspect one session receipt without displaying payloads or changing its pin").action(async (id, options) => {
86374
86399
  try {
86375
86400
  const result2 = inspectSkillSession(id);
86376
- await writeCliOutput(options.json ? JSON.stringify(result2) : `${JSON.stringify(id)}: ${result2.profileId} at ${result2.profileRevision}
86377
- Receipt SHA256: ${result2.receiptSha256}`);
86401
+ await writeCliOutput(options.json ? JSON.stringify(result2) : [
86402
+ `${JSON.stringify(id)}: ${result2.profileId} at ${result2.profileRevision}`,
86403
+ `Receipt SHA256: ${result2.receiptSha256}`,
86404
+ ...result2.selections.map((selection) => `${selection.loaded ? "Loaded" : "Unloaded"}: ${sessionSelectionLabel(selection)}`)
86405
+ ].join(`
86406
+ `));
86378
86407
  } catch (error61) {
86379
86408
  reportContextError(error61, options.json);
86380
86409
  }
@@ -86401,7 +86430,13 @@ Review digest: ${result2.reviewDigest}`);
86401
86430
  try {
86402
86431
  const result2 = await reconcileSkillSession({ ...options, sessionId: id });
86403
86432
  await writeCliOutput(options.json ? JSON.stringify(result2) : result2.applied ? `Reconciled ${JSON.stringify(id)} to ${result2.plan.target.profileId} at ${result2.plan.target.profileRevision}.
86404
- Preserved original receipt: ${result2.archivePath}` : `Planned one session reconciliation. Review --json output, then use --apply --plan-digest ${result2.planDigest} --plan-issued-at ${result2.plan.issuedAt} --plan-expires-at ${result2.plan.expiresAt}.`);
86433
+ Preserved original receipt: ${result2.archivePath}` : [
86434
+ `Planned one session reconciliation: ${result2.plan.retainedLoadedCount} loaded retained, ${result2.plan.retiredLoadedCount} loaded retired.`,
86435
+ ...result2.plan.selectionDelta.map((row) => `${row.outcome} (${row.reason}): ${sessionSelectionLabel(row.selection)}${row.replacement ? ` -> ${sessionSelectionLabel(row.replacement)}` : ""}`),
86436
+ ...result2.plan.addedSelections.map((selection) => `Added selection (unloaded): ${sessionSelectionLabel(selection)}`),
86437
+ `Review --json output, then use --apply --plan-digest ${result2.planDigest} --plan-issued-at ${result2.plan.issuedAt} --plan-expires-at ${result2.plan.expiresAt}.`
86438
+ ].join(`
86439
+ `));
86405
86440
  } catch (error61) {
86406
86441
  reportContextError(error61, options.json);
86407
86442
  }
package/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.10.54",
20022
+ version: "0.10.55",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.54",
28
+ version: "0.10.55",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
24140
24140
  // package.json
24141
24141
  var package_default = {
24142
24142
  name: "@hasna/skills",
24143
- version: "0.10.54",
24143
+ version: "0.10.55",
24144
24144
  description: "Skills library for AI coding agents",
24145
24145
  type: "module",
24146
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24143
24143
  // package.json
24144
24144
  var package_default = {
24145
24145
  name: "@hasna/skills",
24146
- version: "0.10.54",
24146
+ version: "0.10.55",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
package/dist/index.js CHANGED
@@ -11833,7 +11833,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11833
11833
  // package.json
11834
11834
  var package_default = {
11835
11835
  name: "@hasna/skills",
11836
- version: "0.10.54",
11836
+ version: "0.10.55",
11837
11837
  description: "Skills library for AI coding agents",
11838
11838
  type: "module",
11839
11839
  bin: {
@@ -48150,8 +48150,12 @@ function assertPlanWindow(issuedAt, expiresAt, now) {
48150
48150
  throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
48151
48151
  }
48152
48152
  }
48153
+ function selectionMetadata(selection) {
48154
+ return { slug: selection.slug, version: selection.version, bundleDigest: selection.bundleDigest };
48155
+ }
48153
48156
  function inspectSkillSession(sessionId, options = {}) {
48154
48157
  const snapshot2 = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot2;
48158
+ const loaded = new Set(receipt.loaded);
48155
48159
  return {
48156
48160
  sessionId,
48157
48161
  path: snapshot2.path,
@@ -48163,7 +48167,8 @@ function inspectSkillSession(sessionId, options = {}) {
48163
48167
  profileId: receipt.profile.profileId,
48164
48168
  profileRevision: receipt.profile.profileRevision,
48165
48169
  selectionCount: receipt.profile.selections.length,
48166
- loadedCount: receipt.loaded.length
48170
+ loadedCount: receipt.loaded.length,
48171
+ selections: receipt.profile.selections.map((selection) => ({ ...selectionMetadata(selection), loaded: loaded.has(selectionKey(selection)) }))
48167
48172
  };
48168
48173
  }
48169
48174
  var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
@@ -48198,6 +48203,21 @@ async function reconcileSkillSession(input2, options = {}) {
48198
48203
  }
48199
48204
  const targetKeys = new Set(target.selections.map(selectionKey));
48200
48205
  const loaded = old.loaded.filter((key) => targetKeys.has(key));
48206
+ const previouslyLoaded = new Set(old.loaded);
48207
+ const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
48208
+ const previousSlugs = new Set(old.profile.selections.map((selection) => selection.slug));
48209
+ const selectionDelta = old.profile.selections.map((previous) => {
48210
+ const current = targetBySlug.get(previous.slug);
48211
+ const wasLoaded = previouslyLoaded.has(selectionKey(previous));
48212
+ const reason2 = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
48213
+ return {
48214
+ selection: selectionMetadata(previous),
48215
+ loaded: wasLoaded,
48216
+ outcome: !wasLoaded ? "unloaded" : reason2 === "same-bundle" ? "retained" : "retired",
48217
+ reason: reason2,
48218
+ ...reason2 === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
48219
+ };
48220
+ });
48201
48221
  const replacementGeneration = nextSkillSessionGeneration(snapshot2.generation);
48202
48222
  const plan = {
48203
48223
  schemaVersion: 1,
@@ -48210,6 +48230,8 @@ async function reconcileSkillSession(input2, options = {}) {
48210
48230
  replacementGeneration,
48211
48231
  retainedLoadedCount: loaded.length,
48212
48232
  retiredLoadedCount: old.loaded.length - loaded.length,
48233
+ selectionDelta,
48234
+ addedSelections: target.selections.filter((selection) => !previousSlugs.has(selection.slug)).map(selectionMetadata),
48213
48235
  scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
48214
48236
  };
48215
48237
  const planDigest = digest3(plan);
@@ -16,6 +16,19 @@ export interface SessionReconciliationOptions extends SelectionCacheOptions {
16
16
  client?: ProfileClient;
17
17
  }
18
18
  export declare const SESSION_RECONCILIATION_PLAN_TTL_MS: number;
19
+ /** Exact bundle identity only; never spread receipt, policy or payload fields. */
20
+ export interface SessionSelectionMetadata {
21
+ slug: string;
22
+ version: string;
23
+ bundleDigest: string;
24
+ }
25
+ export interface SessionSelectionDelta {
26
+ selection: SessionSelectionMetadata;
27
+ loaded: boolean;
28
+ outcome: "retained" | "retired" | "unloaded";
29
+ reason: "same-bundle" | "selection-removed" | "bundle-changed";
30
+ replacement?: SessionSelectionMetadata;
31
+ }
19
32
  /** Metadata only: inspection never displays skill payloads or resolves credentials. */
20
33
  export declare function inspectSkillSession(sessionId: string, options?: SelectionCacheOptions): {
21
34
  sessionId: string;
@@ -29,6 +42,12 @@ export declare function inspectSkillSession(sessionId: string, options?: Selecti
29
42
  profileRevision: string;
30
43
  selectionCount: number;
31
44
  loadedCount: number;
45
+ selections: {
46
+ loaded: boolean;
47
+ slug: string;
48
+ version: string;
49
+ bundleDigest: string;
50
+ }[];
32
51
  };
33
52
  /** Fits inside the managed context child's 6.5-second ceiling, including CLI work. */
34
53
  export declare const SESSION_RENEWAL_TIMEOUT_MS = 4000;
@@ -75,6 +94,8 @@ export declare function reconcileSkillSession(input: SessionReconciliationInput,
75
94
  replacementGeneration: number;
76
95
  retainedLoadedCount: number;
77
96
  retiredLoadedCount: number;
97
+ selectionDelta: SessionSelectionDelta[];
98
+ addedSelections: SessionSelectionMetadata[];
78
99
  scope: string;
79
100
  };
80
101
  planDigest: string;
@@ -112,6 +133,8 @@ export declare function reconcileSkillSession(input: SessionReconciliationInput,
112
133
  replacementGeneration: number;
113
134
  retainedLoadedCount: number;
114
135
  retiredLoadedCount: number;
136
+ selectionDelta: SessionSelectionDelta[];
137
+ addedSelections: SessionSelectionMetadata[];
115
138
  scope: string;
116
139
  };
117
140
  planDigest: string;
@@ -19,7 +19,7 @@ export { CloudExecutionClient, type CloudExecution, type CloudEligibility } from
19
19
  export { readExecutionGrantPolicy, saveExecutionGrantPolicy, resolveExecutionGrant } from "../lib/execution-grant-client.js";
20
20
  export type { ExecutionGrant, ExecutionGrantPolicy, ExecutionGrantRequest, ResolvedExecutionGrant } from "../lib/execution-grants.js";
21
21
  export { HttpProfileClient, createProfileClient, type ProfileClient, type AuthenticatedProfilePrincipal, type AuthenticatedProfileRole } from "../lib/profile-client.js";
22
- export { inspectSkillSession, reconcileSkillSession, type SessionReconciliationInput, type SessionReconciliationOptions } from "../lib/session-reconciliation.js";
22
+ export { inspectSkillSession, reconcileSkillSession, type SessionSelectionMetadata, type SessionSelectionDelta, type SessionReconciliationInput, type SessionReconciliationOptions } from "../lib/session-reconciliation.js";
23
23
  export { resolveSelectedRun, executeSelectedLocal, prepareSelectedSecretBindings, type ResolvedSelectedRun, type SelectedLocalRunOptions } from "../lib/selected-run.js";
24
24
  export { readSelectedSecretBindings, type SelectedSecretBindings, type SelectedSecretsClient } from "../lib/execution-secrets.js";
25
25
  export type { SkillSelection, SkillProfile, ResolvedSkillSelection, ResolvedSkillProfile, StationSkillStateInput, StationSkillState } from "../types/skill-selection.js";
package/dist/sdk/index.js CHANGED
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
32373
32373
  // package.json
32374
32374
  var package_default = {
32375
32375
  name: "@hasna/skills",
32376
- version: "0.10.54",
32376
+ version: "0.10.55",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
@@ -37490,8 +37490,12 @@ function assertPlanWindow(issuedAt, expiresAt, now) {
37490
37490
  throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
37491
37491
  }
37492
37492
  }
37493
+ function selectionMetadata(selection2) {
37494
+ return { slug: selection2.slug, version: selection2.version, bundleDigest: selection2.bundleDigest };
37495
+ }
37493
37496
  function inspectSkillSession(sessionId, options = {}) {
37494
37497
  const snapshot = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot;
37498
+ const loaded = new Set(receipt.loaded);
37495
37499
  return {
37496
37500
  sessionId,
37497
37501
  path: snapshot.path,
@@ -37503,7 +37507,8 @@ function inspectSkillSession(sessionId, options = {}) {
37503
37507
  profileId: receipt.profile.profileId,
37504
37508
  profileRevision: receipt.profile.profileRevision,
37505
37509
  selectionCount: receipt.profile.selections.length,
37506
- loadedCount: receipt.loaded.length
37510
+ loadedCount: receipt.loaded.length,
37511
+ selections: receipt.profile.selections.map((selection2) => ({ ...selectionMetadata(selection2), loaded: loaded.has(selectionKey(selection2)) }))
37507
37512
  };
37508
37513
  }
37509
37514
  var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
@@ -37538,6 +37543,21 @@ async function reconcileSkillSession(input, options = {}) {
37538
37543
  }
37539
37544
  const targetKeys = new Set(target.selections.map(selectionKey));
37540
37545
  const loaded = old.loaded.filter((key) => targetKeys.has(key));
37546
+ const previouslyLoaded = new Set(old.loaded);
37547
+ const targetBySlug = new Map(target.selections.map((selection2) => [selection2.slug, selection2]));
37548
+ const previousSlugs = new Set(old.profile.selections.map((selection2) => selection2.slug));
37549
+ const selectionDelta = old.profile.selections.map((previous) => {
37550
+ const current = targetBySlug.get(previous.slug);
37551
+ const wasLoaded = previouslyLoaded.has(selectionKey(previous));
37552
+ const reason = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
37553
+ return {
37554
+ selection: selectionMetadata(previous),
37555
+ loaded: wasLoaded,
37556
+ outcome: !wasLoaded ? "unloaded" : reason === "same-bundle" ? "retained" : "retired",
37557
+ reason,
37558
+ ...reason === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
37559
+ };
37560
+ });
37541
37561
  const replacementGeneration = nextSkillSessionGeneration(snapshot.generation);
37542
37562
  const plan = {
37543
37563
  schemaVersion: 1,
@@ -37550,6 +37570,8 @@ async function reconcileSkillSession(input, options = {}) {
37550
37570
  replacementGeneration,
37551
37571
  retainedLoadedCount: loaded.length,
37552
37572
  retiredLoadedCount: old.loaded.length - loaded.length,
37573
+ selectionDelta,
37574
+ addedSelections: target.selections.filter((selection2) => !previousSlugs.has(selection2.slug)).map(selectionMetadata),
37553
37575
  scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
37554
37576
  };
37555
37577
  const planDigest = digest(plan);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.54",
3
+ "version": "0.10.55",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {