agent-skillboard 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/CHANGELOG.md CHANGED
@@ -2,6 +2,47 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.3 — 2026-07-15
6
+
7
+ ### Added
8
+
9
+ - Added deterministic Unicode intent tokenization with whole-run and character
10
+ bigram matching for legacy v1 compatibility routing.
11
+
12
+ ### Changed
13
+
14
+ - Removed v2 request tokenization, metadata scoring, ranked hints, and
15
+ deterministic recommendations. Route and brief now return raw eligible skill
16
+ descriptions and saved preferences for the active model to interpret.
17
+ - Made route, can-use, and guard errors explain whether the detected policy
18
+ expects v2 `--agent` or v1 `--workflow`, including the v1 migration preview.
19
+
20
+ ### Fixed
21
+
22
+ - Preserved legacy v1 explicit skill-id selection when an ASCII id is
23
+ immediately followed by a Hangul or CJK particle, without allowing Latin
24
+ prefix matches.
25
+
26
+ ## 0.3.2 — 2026-07-14
27
+
28
+ ### Changed
29
+
30
+ - Made global npm updates and `skillboard setup` automatically upgrade a valid
31
+ version 1 user policy when every migration choice is already understood.
32
+ - Kept the explicit `migrate v2` preview, apply, and rollback commands for
33
+ project policies, audits, and recovery.
34
+
35
+ ### Fixed
36
+
37
+ - Removed the hidden manual migration step left after a 0.3.1 update while
38
+ preserving explicit terminal denials as disabled and review-only quarantine
39
+ states as enabled, agent-local skills.
40
+ - Kept automatic migration non-mutating for unknown future ambiguity kinds or
41
+ policy skills that are not currently observed, and restored exact version 1
42
+ bytes after any transactional failure.
43
+ - Restored invoking-user ownership for migration backups, manifests, and
44
+ generated inventory during sudo-driven global updates.
45
+
5
46
  ## 0.3.1 — 2026-07-14
6
47
 
7
48
  ### Added
package/README.md CHANGED
@@ -6,10 +6,11 @@ SkillBoard gives AI agents one small user-level policy for installed skills:
6
6
  2. Keep an enabled skill agent-local or shared across agents.
7
7
 
8
8
  Valid installed skills default to enabled and agent-local. Sharing is opt-in per
9
- skill. Optional preference ranks enabled skills installed for the current agent
10
- and never changes availability or copies files.
9
+ skill. The model makes semantic skill choices from the request, eligible skill
10
+ descriptions, and raw saved preferences. SkillBoard does not tokenize, score,
11
+ match, or recommend from v2 request text.
11
12
 
12
- Status: public alpha. Package 0.3.1 writes policy schema v2.
13
+ Status: public alpha. Package 0.3.3 writes policy schema v2.
13
14
 
14
15
  <p align="center">
15
16
  <img src="https://raw.githubusercontent.com/NyXXiR/skillboard/main/skillboard.png" alt="SkillBoard architecture diagram" width="100%">
@@ -56,6 +57,14 @@ prefix, activate the Node environment that owns each stale copy, and run
56
57
  uninstall another prefix. Restart or refresh agents after an update because
57
58
  some agents cache user skills.
58
59
 
60
+ When an update finds a valid version 1 user policy, setup automatically migrates
61
+ it only when all reported choices are understood. It creates an adjacent exact
62
+ backup, keeps explicit terminal denials disabled, and maps review-only
63
+ quarantine states to enabled and agent-local. Unknown future ambiguity leaves
64
+ the policy unchanged and prints the explicit preview command instead. The same
65
+ review path is used when a v1 policy skill is not currently observed, avoiding
66
+ an unhealthy generated inventory or silently forgetting a denial.
67
+
59
68
  `setup` is safe to rerun. It refreshes managed guidance and inventory, discovers
60
69
  late standard agent homes and Hermes profiles, and fills already-selected
61
70
  `shared: true` skills into newly active roots. It does not make every skill
@@ -120,8 +129,9 @@ destructive actions, and secrets.
120
129
  From any working directory, an agent can:
121
130
 
122
131
  1. Read `skillboard brief --intent <request> --agent <agent> --json`.
123
- 2. Select an enabled skill installed for that agent, using preference only to
124
- rank matches.
132
+ 2. Select the best eligible skill from the full request, descriptions, local
133
+ instructions, and raw saved preferences, or use no skill. The model itself
134
+ interprets explicit user direction and preference semantics.
125
135
  3. Run `skillboard guard use <skill-id> --agent <agent> --json` before use.
126
136
  4. Work without another approval when the guard allows use.
127
137
  5. Ask after completion only if remembering a preference would help.
@@ -143,8 +153,8 @@ current action, runs `skillboard apply-action <action-id> --agent <agent> --yes
143
153
  rereads the returned post-apply brief.
144
154
 
145
155
  This is the distinction from a pure distribution tool: skills stay local by
146
- default, users promote only selected skills to shared use, and routing preference
147
- can be remembered without changing installation or availability.
156
+ default, users promote only selected skills to shared use, and a model-facing
157
+ preference can be remembered without changing installation or availability.
148
158
 
149
159
  ## Migrate version 1
150
160
 
@@ -157,9 +167,16 @@ skillboard migrate v2 --config <path> --yes --json
157
167
  skillboard migrate v2 --config <path> --rollback <backup> --json
158
168
  ```
159
169
 
160
- Setup and package updates keep v1 bytes unchanged and print the preview command.
161
- SkillBoard does not automatically migrate version 1. All v1 mutations refuse
162
- and point to migration. v0.4.0 removes the v1 reader.
170
+ Starting in 0.3.2, setup and global package updates automatically migrate a
171
+ valid version 1 user policy when the report contains only understood choices.
172
+ The transaction creates an adjacent exact backup. Explicit terminal denials
173
+ remain disabled; review-only quarantine becomes enabled and agent-local. An
174
+ unknown future ambiguity leaves the policy unchanged and prints the preview
175
+ command. A policy skill that is not currently observed also stays on v1 for an
176
+ explicit decision, because forgetting a disabled entry could enable it if it
177
+ later reappears. The explicit commands remain available for project policies,
178
+ audits, and rollback. Other v1 mutations still refuse and point to migration.
179
+ v0.4.0 removes the v1 reader.
163
180
 
164
181
  ## Cleanup
165
182
 
@@ -7,16 +7,23 @@ cross-agent sharing, and overlap routing. It asks only two policy questions:
7
7
  2. Should SkillBoard share this skill with other supported agents?
8
8
 
9
9
  Installed, valid skills default to enabled and agent-local. A user may disable
10
- a skill or opt that individual skill into sharing. An optional preference ranks
11
- enabled candidates that are actually installed for the current agent;
12
- preference never changes availability or copies files.
10
+ a skill or opt that individual skill into sharing. An optional preference is
11
+ returned as raw context beside eligible skills; it never changes availability
12
+ or copies files.
13
+
14
+ The active model owns all v2 semantic skill selection. SkillBoard returns the
15
+ request unchanged with enabled skills installed for the current agent, their
16
+ descriptions, and raw saved preferences. It does not tokenize, normalize,
17
+ score, match, or recommend from v2 request text. The model interprets Korean,
18
+ explicit user direction, and preferences directly, and may choose no skill.
13
19
 
14
20
  ## Product loop
15
21
 
16
22
  Preserve `observe → route → work → explain briefly → ask after → remember`.
17
23
  Ordinary work must not stop for source review or pre-task configuration. Read
18
- the current brief when skills overlap, run the guard before use, and ask
19
- once only before a persistent policy change.
24
+ the current brief when skills overlap, make the semantic choice in the active
25
+ model, run the guard before use, and ask once only before a persistent policy
26
+ change.
20
27
 
21
28
  ## Boundaries
22
29
 
@@ -45,13 +52,18 @@ inventory, and materializes missing copies only for skills already marked
45
52
  discovery state. They must not introduce project scope, source trust gates, or
46
53
  another authorization layer.
47
54
 
55
+ When setup encounters version 1 user policy, it may converge that policy to v2
56
+ only if every migration ambiguity is a known review-only quarantine mapping.
57
+ It must preserve terminal denials, create an exact recoverable backup, and leave
58
+ unknown future ambiguity unchanged for an explicit decision.
59
+
48
60
  ## Development rule
49
61
 
50
62
  Every route, brief, guard, dashboard, generated bridge, and policy mutation must
51
63
  project the same v2 meaning. Normal commands use one home control plane from any
52
64
  working directory; project initialization is not part of the v2 flow. Never
53
- reintroduce source reputation, workflow scope, or legacy state fields as hidden
54
- authorization checks.
65
+ reintroduce source reputation, workflow scope, v2 tokenizers or metadata scores,
66
+ or legacy state fields as hidden authorization or semantic-selection checks.
55
67
 
56
68
  Version 1 is migration input only. It has a one-release read-only window in the
57
69
  v0.3 release and is removed in v0.4.0.
package/docs/install.md CHANGED
@@ -35,6 +35,15 @@ keep. Activate the Node environment that owns each stale prefix, confirm it with
35
35
  SkillBoard does not automatically uninstall another prefix or request elevated
36
36
  permissions for cleanup.
37
37
 
38
+ From 0.3.2, setup and global postinstall automatically migrate a valid version
39
+ 1 user policy when every reported migration choice is understood. Migration
40
+ creates an adjacent exact backup, preserves explicit terminal denials as
41
+ disabled, and makes review-only quarantine states enabled and agent-local. An
42
+ unknown future ambiguity leaves the policy unchanged and prints the explicit
43
+ preview command; no migration files are changed. A v1 policy skill that is not
44
+ currently observed uses the same review path so setup neither invents inventory
45
+ nor silently forgets its policy.
46
+
38
47
  Run setup later when lifecycle scripts were skipped, another agent or Hermes
39
48
  profile was added, or a managed root needs to be repaired:
40
49
 
@@ -104,10 +113,12 @@ skillboard migrate v2 --config <path> --yes --json
104
113
  skillboard migrate v2 --config <path> --rollback <backup> --json
105
114
  ```
106
115
 
107
- Version 1 is read-only during v0.3.x. v0.4.0 removes the v1 reader.
108
- Setup and postinstall preserve an existing v1 policy byte-for-byte and suggest
109
- the preview command only. SkillBoard does not automatically migrate version 1;
110
- apply and rollback remain explicit user actions.
116
+ Version 1 is read-only during v0.3.x. v0.4.0 removes the v1 reader. Setup and
117
+ global postinstall automatically migrate an understood version 1 user policy
118
+ through the same backup and validation transaction. Unknown future ambiguity
119
+ or a policy skill that is not currently observed leaves it unchanged and prints
120
+ the preview command. Explicit preview, apply, and rollback remain available for
121
+ project policies, audits, and recovery.
111
122
 
112
123
  ## Uninstall
113
124
 
@@ -24,7 +24,7 @@ compatible copies for other supported agents.
24
24
 
25
25
  ## Optional routing preference
26
26
 
27
- Preference ranks enabled skills installed for the current agent. It never
27
+ Preference is raw context shown to the model for an eligible skill. It never
28
28
  changes availability and never shares files.
29
29
 
30
30
  ```yaml
@@ -38,8 +38,10 @@ changes availability and never shares files.
38
38
  priority: 100
39
39
  ```
40
40
 
41
- An explicit user selection wins when the guard allows it. A high priority cannot
42
- enable a disabled skill or make a skill appear on an agent where it is absent.
41
+ The model interprets explicit user selection and saved preference semantics from
42
+ the full request. SkillBoard does not match the `intents` strings or use
43
+ `priority` as a score in v2. A high priority cannot enable a disabled skill or
44
+ make a skill appear on an agent where it is absent.
43
45
 
44
46
  ## Generated inventory
45
47
 
@@ -71,3 +73,10 @@ skillboard migrate v2 --config <path> --rollback <backup> --json
71
73
  ```
72
74
 
73
75
  v0.4.0 removes the v1 reader.
76
+
77
+ Setup and global postinstall automatically upgrade a valid version 1 user
78
+ policy when its migration report contains only understood choices. The
79
+ transaction keeps an exact adjacent backup. An unknown future ambiguity leaves
80
+ the policy unchanged and requires the explicit preview/apply flow. Policy skills
81
+ that are not currently observed also require review rather than being silently
82
+ forgotten.
@@ -8,8 +8,10 @@ Its durable policy answers two questions:
8
8
  1. Is this skill enabled?
9
9
  2. Should this one skill remain agent-local or be shared across agents?
10
10
 
11
- Optional preference ranks enabled skills installed for the current agent and
12
- never changes availability.
11
+ The active model chooses the best enabled skill installed for the current
12
+ agent, or no skill, from raw descriptions and optional saved preferences.
13
+ SkillBoard does not tokenize, score, or match v2 request text. Preference never
14
+ changes availability.
13
15
 
14
16
  ## Difference from distribution tools
15
17
 
package/docs/reference.md CHANGED
@@ -16,9 +16,9 @@ Source tree: replace `skillboard ` with `node bin/skillboard.mjs `.
16
16
  skillboard setup [--yes] [--agent codex[,claude,opencode,hermes]] [--skill-root <path>]
17
17
  skillboard inventory refresh [--config <path>] [--dry-run] [--json]
18
18
  skillboard brief [--agent codex|claude|opencode|hermes] [--intent <request>] [--include-actions] [--json]
19
- skillboard route <intent> --agent codex|claude|opencode|hermes [--json]
20
- skillboard can-use <skill-id> --agent codex|claude|opencode|hermes [--json]
21
- skillboard guard use <skill-id> --agent codex|claude|opencode|hermes [--json]
19
+ skillboard route <intent> --agent <name> (v2 policy) | --workflow <name> (v1 policy) [--json]
20
+ skillboard can-use <skill-id> --agent <name> (v2 policy) | --workflow <name> (v1 policy) [--json]
21
+ skillboard guard use <skill-id> --agent <name> (v2 policy) | --workflow <name> (v1 policy) [--json]
22
22
  skillboard skill enable <skill-id> [--dry-run] [--json]
23
23
  skillboard skill disable <skill-id> [--dry-run] [--json]
24
24
  skillboard skill share <skill-id> [--dry-run] [--json]
@@ -50,12 +50,18 @@ non-symlinked path inside the invoking user's home, and persists the mapping in
50
50
  not authorization or policy. One root cannot be registered to two agents.
51
51
 
52
52
  Guard requires a valid inventory record, `enabled: true`, and the selected agent
53
- in `installed_on`. Optional preference ranks candidates but never changes
54
- availability.
53
+ in `installed_on`. For v2 routing, SkillBoard returns the request plus raw
54
+ eligible skill descriptions and saved preferences. It does not tokenize, score,
55
+ match, or recommend from request text. The active model interprets explicit
56
+ direction and preferences, chooses a skill or no skill, then runs guard.
57
+ Preference never changes availability.
55
58
 
56
59
  Version 2 route, can-use, and guard require `--agent`; generated agent guidance
57
60
  passes it automatically. This prevents one agent from selecting another
58
61
  agent's local-only skill without adding a user prompt to the normal flow.
62
+ Version 1 compatibility policy uses `--workflow` instead. Passing the selector
63
+ for the other policy version exits with guidance for the detected version and,
64
+ for v1, the exact `migrate v2` preview command.
59
65
 
60
66
  ## Config schema v2
61
67
 
@@ -79,8 +85,8 @@ skills:
79
85
  - `enabled` and `shared` are required Booleans.
80
86
  - `shared: false` means agent-local; `shared: true` enables managed cross-agent
81
87
  copies while preserving the original.
82
- - `preference` is optional and ranks enabled skills installed for the current
83
- agent only.
88
+ - `preference` is optional raw context for model selection when the skill is
89
+ enabled and installed for the current agent. SkillBoard does not score it.
84
90
  - Unknown policy keys are rejected.
85
91
  - Skill ids use letters, numbers, `.`, `_`, `:`, and `-`; path-like ids are rejected.
86
92
  - A valid newly discovered skill defaults to enabled and agent-local.
@@ -153,8 +159,13 @@ Preview changes no bytes. Apply creates an adjacent byte-for-byte backup and
153
159
  writes v2 policy plus generated inventory atomically. Rollback restores the
154
160
  selected backup. v0.4.0 removes the v1 reader.
155
161
 
156
- Setup and npm postinstall preserve an existing v1 policy and print only the
157
- preview form. SkillBoard does not automatically migrate version 1.
162
+ Setup and global npm postinstall automatically migrate a valid version 1 user
163
+ policy when all reported choices are understood. They create an adjacent exact
164
+ backup, preserve terminal denials as disabled, and map review-only quarantine
165
+ to enabled and agent-local. An unknown future ambiguity leaves the policy
166
+ unchanged, changes no migration files, and prints the preview form. The same
167
+ review path names policy skills that are not currently observed. Explicit
168
+ commands remain available for project policies, audit, and recovery.
158
169
 
159
170
  Legacy fields are interpreted only by migration and never become hidden v2
160
171
  authorization. Primary examples are `examples/v2-multi-source.config.yaml` and
package/docs/routing.md CHANGED
@@ -4,16 +4,22 @@ Routing chooses among skills already available to the current agent. Guard
4
4
  decides availability from valid generated inventory, `enabled`, and
5
5
  `installed_on`; routing cannot override that verdict.
6
6
 
7
- ## Resolution order
7
+ ## v2 resolution order
8
8
 
9
9
  1. Remove skills missing valid inventory records.
10
10
  2. Remove disabled skills.
11
11
  3. Remove skills not installed for the selected agent.
12
- 4. Honor an explicit user-selected skill if it remains eligible.
13
- 5. Rank intent matches by optional preference and priority.
14
- 6. Return one recommendation and ordered fallbacks.
15
-
16
- Preference ranks only and never changes availability.
12
+ 4. Return every remaining skill's id, name, description, path, and raw optional
13
+ preference beside the request.
14
+ 5. Let the active model interpret the full request, explicit user direction,
15
+ descriptions, instructions, and preferences and choose a skill or no skill.
16
+ 6. Guard the model-selected skill immediately before use.
17
+
18
+ V2 has no intent tokenizer, metadata score, request matcher, ranked candidates,
19
+ or deterministic recommendation. Korean and other languages are handled by the
20
+ foundation model. Preference is raw model context and never changes
21
+ availability. Version 1 compatibility routing retains its deterministic
22
+ tokenizer until the v1 reader is removed in v0.4.0.
17
23
 
18
24
  ```bash
19
25
  skillboard brief --intent "write tests" --agent codex --json
@@ -26,8 +32,13 @@ These commands use `~/skillboard.config.yaml` and
26
32
  `--config` and `--skills` overrides remain available for migration, testing,
27
33
  and legacy workspaces.
28
34
 
29
- When guard allows use, continue without another approval. Ask after completion
30
- only when remembering a preference would reduce future ambiguity.
35
+ When guard allows use, continue without another approval. If the model-selected
36
+ choice was ambiguous, finish first and ask afterward only when remembering a
37
+ preference would reduce future ambiguity.
38
+
39
+ Version 1 compatibility routing remains workflow-driven and may return a
40
+ deterministic capability route. It asks for clarification when no workflow
41
+ route matches.
31
42
 
32
43
  Source and provenance findings are optional audit metadata and never determine
33
44
  availability. Runtime and action authorization are outside SkillBoard's scope.
package/docs/user-flow.md CHANGED
@@ -41,8 +41,11 @@ skillboard guard use <skill-id> --agent codex --json
41
41
  ```
42
42
 
43
43
  The guard allows an enabled skill only when generated inventory records it on
44
- the selected agent. Preference ranks matching candidates and never changes
45
- availability. Allowed use does not require another confirmation.
44
+ the selected agent. SkillBoard returns raw eligible descriptions and saved
45
+ preferences without tokenizing, scoring, or matching the request. The model
46
+ interprets the full request, explicit direction, and preferences and chooses a
47
+ skill or no skill. Preference never changes availability. Allowed use does not
48
+ require another confirmation.
46
49
 
47
50
  ## 3. Change one decision
48
51
 
@@ -65,9 +68,10 @@ returned post-apply brief. Cached action ids are not reused.
65
68
 
66
69
  ## 4. Ask after, then remember
67
70
 
68
- When a route was ambiguous, the agent finishes first. It may then ask whether to
69
- remember an intent preference. Preference affects ordering only; it does not
70
- share, enable, disable, install, or remove a skill.
71
+ When a model-selected route was ambiguous, the agent finishes first. It may then
72
+ ask whether to remember an intent preference. Preference becomes raw context
73
+ for later model selection; it does not share, enable, disable, install, or
74
+ remove a skill.
71
75
 
72
76
  ## 5. Audit separately
73
77
 
@@ -111,4 +115,8 @@ skillboard migrate v2 --config <path> --rollback <backup> --json
111
115
  ```
112
116
 
113
117
  Version 1 remains readable but immutable for the v0.3.x one-release read-only
114
- window. v0.4.0 removes the v1 reader. No ordinary command migrates implicitly.
118
+ window. Setup and global package updates automatically migrate an understood
119
+ version 1 user policy with an exact adjacent backup. Unknown future ambiguity
120
+ or a policy skill that is not currently observed leaves it unchanged and prints
121
+ the preview command. Project policies and other ordinary commands still require
122
+ explicit migration. v0.4.0 removes the v1 reader.
@@ -12,10 +12,15 @@ node --test test/v2-agent-sharing.test.mjs test/v2-onboarding-behavior.test.mjs
12
12
  - Valid discovered skills default to enabled and agent-local.
13
13
  - Disabled skills and skills absent from the selected agent are denied.
14
14
  - Share and unshare preserve agent-owned originals.
15
- - Optional preference changes ranking without changing availability.
15
+ - V2 returns raw eligible descriptions and preferences without interpreting
16
+ request text or producing a recommendation.
17
+ - Korean, explicit skill direction, and saved preference semantics remain owned
18
+ by the active model.
19
+ - Optional preference never changes availability.
16
20
  - Source and provenance audits do not change guard results.
17
21
  - Commands produce the same result from different working directories.
18
- - Version 1 is read-only until explicit migration.
22
+ - Version 1 project policy remains read-only until explicit migration; setup
23
+ automatically migrates an understood user policy with an exact backup.
19
24
 
20
25
  ## Reproduce the user surface
21
26
 
@@ -3,7 +3,8 @@
3
3
  A variant is a manual content adaptation with snapshot and digest history. The
4
4
  lifecycle does not authorize availability. A registered variant is usable only
5
5
  according to its ordinary v2 `enabled` entry and generated installation
6
- presence; optional preference ranks it and never changes availability.
6
+ presence; optional preference is raw model context and never changes
7
+ availability.
7
8
 
8
9
  ## Current v2 boundary
9
10
 
@@ -22,9 +22,13 @@ Preview is non-mutating, apply creates an adjacent byte-for-byte backup, and
22
22
  rollback restores the selected backup. Old action ids, hooks, and lock
23
23
  projections are invalid after migration.
24
24
 
25
- Package install, update, setup, and doctor never automatically migrate a v1
26
- policy. Setup preserves its bytes and prints the preview command so the policy
27
- decision stays separate from package maintenance.
25
+ Starting in 0.3.2, setup and global package updates automatically migrate a
26
+ valid version 1 user policy when all migration choices are understood. They use
27
+ the same exact-backup, atomic-write, validation, and recovery transaction as
28
+ the explicit command. Unknown future ambiguity leaves the policy unchanged and
29
+ prints the preview command. A policy skill that is not currently observed also
30
+ requires review, preserving future denial semantics. Doctor and project-local
31
+ commands remain non-mutating.
28
32
 
29
33
  ## Versioned v2 contract
30
34
 
@@ -39,7 +43,8 @@ schema.
39
43
  1. Run `npm run check`.
40
44
  2. Run `npm pack --dry-run --json` and inspect public contents.
41
45
  3. Install the tarball into an isolated prefix and run
42
- `skillboard doctor --summary`; verify fresh setup, v1 preservation, and a
46
+ `skillboard doctor --summary`; verify fresh setup, safe v1 auto-migration,
47
+ exact backup preservation, and a
43
48
  late-agent shared-skill reconciliation.
44
49
  4. Confirm `CHANGELOG.md` includes the package version.
45
50
  5. Push `main` and wait for the complete cross-platform check matrix.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-skillboard",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Keep agent skills broadly available while routing overlaps consistently.",
5
5
  "keywords": [
6
6
  "ai-agent",
@@ -3,7 +3,7 @@ import { command } from "./action-core.mjs";
3
3
  const GUARD_WHEN = "immediately before skill use";
4
4
  const GOAL_DOCUMENT = Object.freeze({
5
5
  path: "docs/ai-skill-routing-goal.md",
6
- purpose: "Preserve SkillBoard v2 as a user-level control plane: enable or disable a skill, opt individual skills into cross-agent sharing, and use optional preference only to rank available overlaps.",
6
+ purpose: "Preserve SkillBoard v2 as a user-level control plane: enable or disable a skill, opt individual skills into cross-agent sharing, and expose optional preference as raw context for the active model.",
7
7
  loop: Object.freeze([
8
8
  "observe",
9
9
  "route",
@@ -140,14 +140,22 @@ function recommendedNextStep(status, brief, choices, route = null) {
140
140
  switch (status) {
141
141
  case "ready":
142
142
  if (route !== null) {
143
+ if (route.model_selection_required === true) {
144
+ return modelSelectionStep(route);
145
+ }
143
146
  return route.recommended_skill === null
144
- ? "Ask a clarifying question; no workflow capability matched this request."
147
+ ? route.workflow === null
148
+ ? "No enabled skill is available for this request."
149
+ : "Ask a clarifying question; no workflow capability matched this request."
145
150
  : `Use ${route.recommended_skill} for this request after the guard check passes${postUsePolicyStep(route)}.`;
146
151
  }
147
152
  return availableSkillCount(brief) === 0
148
153
  ? "No policy change is required; valid installed skills default to enabled and agent-local."
149
154
  : "Run the guard check immediately before using any selected skill.";
150
155
  case "needs-decision":
156
+ if (route?.model_selection_required === true) {
157
+ return `${modelSelectionStep(route)} Handle pending review decisions after the task unless a policy-changing action is needed now.`;
158
+ }
151
159
  if (route?.recommended_skill !== null && route?.guard_allowed === true) {
152
160
  return route.post_use_policy_suggestion === null
153
161
  ? `Use ${route.recommended_skill} for this request after the guard check passes; handle pending review decisions after the task unless a policy-changing action is needed now.`
@@ -180,6 +188,10 @@ function recommendedNextStep(status, brief, choices, route = null) {
180
188
  }
181
189
  }
182
190
 
191
+ function modelSelectionStep(route) {
192
+ return "Choose the best enabled, installed skill from the request's semantic context, skill descriptions, and raw saved preferences. Run its guard before use, or continue without a skill when none fits.";
193
+ }
194
+
183
195
  function postUsePolicyStep(route) {
184
196
  return route.post_use_policy_suggestion === null
185
197
  ? ""
@@ -197,6 +209,8 @@ function routeGuidance(route) {
197
209
  matched_terms: route.matched_terms,
198
210
  recommendation_reason: route.recommendation_reason,
199
211
  recommended_skill: route.recommended_skill,
212
+ selection_mode: route.selection_mode ?? null,
213
+ model_selection_required: route.model_selection_required ?? false,
200
214
  fallback_skills: route.fallback_skills,
201
215
  route_candidates: (route.route_candidates ?? []).map((candidate) => ({
202
216
  skill: candidate.skill,
@@ -216,6 +230,10 @@ function routeGuidance(route) {
216
230
  guard_reasons: route.guard?.reasons ?? [],
217
231
  possible_skills: route.possible_skills.map((skill) => ({
218
232
  id: skill.id,
233
+ name: skill.name,
234
+ description: skill.description,
235
+ path: skill.path,
236
+ preference: skill.preference,
219
237
  category: skill.category,
220
238
  allowed: skill.allowed
221
239
  }))
@@ -1,6 +1,7 @@
1
- import { isAbsolute, relative, resolve } from "node:path";
1
+ import { resolve } from "node:path";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import { createInterface } from "node:readline";
4
+ import { commandPrefix, shellQuote } from "./agent-integration-command.mjs";
4
5
  import { installAgentIntegration, uninstallAgentIntegration } from "./agent-integration-files.mjs";
5
6
  import { applyOwnership, applyOwnershipTree, resolveSetupHome, setupOwnership } from "./agent-integration-home.mjs";
6
7
  import { setupAgentSkillTargets, supportedAgentNames } from "./agent-skill-roots.mjs";
@@ -13,6 +14,7 @@ import {
13
14
  } from "./agent-root-registry.mjs";
14
15
  import { refreshAgentInventory } from "./inventory-refresh.mjs";
15
16
  import { reconcileSharedSkills } from "./shared-skill-reconcile.mjs";
17
+ import { upgradeLegacyUserPolicy } from "./setup-policy-migration.mjs";
16
18
 
17
19
  export async function runSetupCommand(options, stdout, runtime = defaultRuntime()) {
18
20
  assertSetupOptions(options);
@@ -52,7 +54,21 @@ export async function runSetupCommand(options, stdout, runtime = defaultRuntime(
52
54
  preserveLegacyPolicy: true
53
55
  });
54
56
  const configPath = resolve(home, inventory.configPath);
55
- const inventoryPath = inventory.inventoryPath === null ? null : resolve(home, inventory.inventoryPath);
57
+ let inventoryPath = inventory.inventoryPath === null ? null : resolve(home, inventory.inventoryPath);
58
+ const policyUpgrade = await upgradeLegacyUserPolicy({
59
+ home,
60
+ configPath,
61
+ inventoryPath,
62
+ observedSkillIds: inventory.observedSkillIds,
63
+ failpoint: runtime.migrationFailpoint
64
+ });
65
+ if (policyUpgrade.status === "upgraded") {
66
+ inventoryPath = policyUpgrade.inventoryPath;
67
+ await applyOwnership(configPath, ownership);
68
+ await applyOwnership(inventoryPath, ownership);
69
+ for (const artifact of policyUpgrade.artifacts) await applyOwnership(artifact, ownership);
70
+ inventory = await refreshAgentInventory({ root: home, home, env, registeredRoots });
71
+ }
56
72
  const shared = inventoryPath === null
57
73
  ? { created: [], unchanged: [], preserved: [], blocked: [] }
58
74
  : await reconcileSharedSkills({ home, env, targets, configPath, inventoryPath });
@@ -80,14 +96,24 @@ export async function runSetupCommand(options, stdout, runtime = defaultRuntime(
80
96
  writeList(stdout, "Blocked shared copies", shared.blocked.map(formatBlockedEntry));
81
97
  stdout.write(`User policy: ${inventory.configPath}\n`);
82
98
  stdout.write(`Observed skills: ${inventory.scan.scannedSkills}\n`);
83
- if (inventory.inventoryPath === null) {
84
- stdout.write("Policy version 1 remains read-only during SkillBoard v0.3.x.\n");
99
+ if (policyUpgrade.status === "upgraded") {
100
+ stdout.write("User policy upgraded automatically to version 2.\n");
101
+ stdout.write(`Backup: ${policyUpgrade.backupPath}\n`);
102
+ } else if (policyUpgrade.status === "decision-required") {
103
+ stdout.write("Policy version 1 needs review before migration; no migration files were changed.\n");
104
+ if (policyUpgrade.unobservedSkillIds.length > 0) {
105
+ stdout.write(`Policy skills not currently observed: ${formatList(policyUpgrade.unobservedSkillIds)}\n`);
106
+ }
85
107
  stdout.write(`Preview migration: ${commandPrefix(runtime)} migrate v2 --config ${shellQuote(configPath, runtime.platform)} --json\n`);
86
108
  }
87
109
  stdout.write("Next:\n");
88
110
  stdout.write("- Restart or refresh agents that cache user skills.\n");
89
111
  stdout.write("- Run skillboard doctor --summary to check policy and executable paths.\n");
90
- stdout.write("- User-level policy and inventory were refreshed; no project was initialized.\n");
112
+ if (policyUpgrade.status === "decision-required") {
113
+ stdout.write("- User-level policy was preserved and installed skills were rescanned; no project was initialized.\n");
114
+ } else {
115
+ stdout.write("- User-level policy and inventory were refreshed; no project was initialized.\n");
116
+ }
91
117
  stdout.write('- Ask the agent in a workspace: "Review this plan and point out weak assumptions."\n');
92
118
  stdout.write("- SkillBoard will step in when skills overlap, routing is ambiguous, or you ask for a skill decision.\n");
93
119
  return 0;
@@ -222,46 +248,3 @@ function formatSharedEntry(entry) {
222
248
  function formatBlockedEntry(entry) {
223
249
  return `${entry.agent ?? "unknown"}:${entry.skill}:${entry.reason}`;
224
250
  }
225
-
226
- function commandPrefix(runtime) {
227
- const entrypoint = runtime.entrypointPath ?? "";
228
- const normalized = entrypoint.replace(/\\/g, "/");
229
- if (normalized.includes("/_npx/")) {
230
- const packageSpec = runtime.packageSpec ?? "agent-skillboard";
231
- return `npx --yes --package ${shellQuote(packageSpec, runtime.platform)} skillboard`;
232
- }
233
- if (isSourceTreeEntrypoint(entrypoint)) {
234
- return `node ${sourceTreeEntrypoint(entrypoint, runtime.cwd ?? process.cwd(), runtime.platform)}`;
235
- }
236
- return "skillboard";
237
- }
238
-
239
- function isSourceTreeEntrypoint(entrypoint) {
240
- if (entrypoint === "") {
241
- return false;
242
- }
243
- const normalized = entrypoint.replace(/\\/g, "/");
244
- return (normalized === "bin/skillboard.mjs" || normalized.endsWith("/bin/skillboard.mjs"))
245
- && !normalized.includes("/node_modules/")
246
- && !normalized.includes("/_npx/")
247
- && !normalized.includes("/.npm/");
248
- }
249
-
250
- function sourceTreeEntrypoint(entrypoint, cwd, platform) {
251
- const absoluteEntrypoint = isAbsolute(entrypoint) ? entrypoint : resolve(cwd, entrypoint);
252
- const relativeEntrypoint = relative(cwd, absoluteEntrypoint).replace(/\\/g, "/");
253
- if (!relativeEntrypoint.startsWith("../") && relativeEntrypoint !== ".." && !isAbsolute(relativeEntrypoint)) {
254
- return shellQuote(relativeEntrypoint, platform);
255
- }
256
- return shellQuote(absoluteEntrypoint, platform);
257
- }
258
-
259
- function shellQuote(value, platform = process.platform) {
260
- if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) {
261
- return value;
262
- }
263
- if (platform === "win32") {
264
- return `"${value.replace(/"/g, '""')}"`;
265
- }
266
- return `'${value.replace(/'/g, "'\\''")}'`;
267
- }