agent-skillboard 0.3.3 → 0.3.4
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 +10 -0
- package/README.md +10 -1
- package/docs/policy-model.md +9 -0
- package/docs/reference.md +11 -0
- package/package.json +1 -1
- package/src/advisor/schema.mjs +2 -1
- package/src/brief-renderer.mjs +14 -0
- package/src/cli.mjs +4 -0
- package/src/doctor.mjs +13 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.4 — 2026-07-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Separated generated-inventory integrity failures from sorted stale
|
|
10
|
+
removed-skill policy observations. Valid inventory now keeps doctor, status,
|
|
11
|
+
brief, strict health, and observed-skill routing healthy while absent skills
|
|
12
|
+
remain guard-denied and available for explicit, confirmation-required policy
|
|
13
|
+
forget actions.
|
|
14
|
+
|
|
5
15
|
## 0.3.3 — 2026-07-15
|
|
6
16
|
|
|
7
17
|
### Added
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ skill. The model makes semantic skill choices from the request, eligible skill
|
|
|
10
10
|
descriptions, and raw saved preferences. SkillBoard does not tokenize, score,
|
|
11
11
|
match, or recommend from v2 request text.
|
|
12
12
|
|
|
13
|
-
Status: public alpha. Package 0.3.
|
|
13
|
+
Status: public alpha. Package 0.3.4 writes policy schema v2.
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
16
|
<img src="https://raw.githubusercontent.com/NyXXiR/skillboard/main/skillboard.png" alt="SkillBoard architecture diagram" width="100%">
|
|
@@ -192,6 +192,15 @@ skillboard skill forget <skill-id>
|
|
|
192
192
|
`forget` never deletes skill files. It refuses skills that are still observed or
|
|
193
193
|
shared, so shared skills must be unshared first.
|
|
194
194
|
|
|
195
|
+
A valid generated inventory remains healthy when it no longer observes an
|
|
196
|
+
unshared policy entry. Doctor/status expose sorted entries as
|
|
197
|
+
`inventory.stalePolicySkills`; brief exposes the same observation as
|
|
198
|
+
`health.inventory.stale_policy_skills`. Stale removed-skill policy does not fail
|
|
199
|
+
strict health, change passed mode, or block other observed skills. The absent
|
|
200
|
+
skill itself remains unavailable to route and guard. SkillBoard never deletes
|
|
201
|
+
these entries automatically: review one current forget action, confirm it, and
|
|
202
|
+
then reread the returned brief.
|
|
203
|
+
|
|
195
204
|
Before removing the SkillBoard package, preview and apply the complete
|
|
196
205
|
user-level cleanup:
|
|
197
206
|
|
package/docs/policy-model.md
CHANGED
|
@@ -54,6 +54,15 @@ optional audit metadata and never determine availability. Runtime and action
|
|
|
54
54
|
authorization are outside SkillBoard's scope and remain with the agent or
|
|
55
55
|
harness.
|
|
56
56
|
|
|
57
|
+
Generated-inventory integrity is separate from stale removed-skill policy.
|
|
58
|
+
Missing, malformed, unsupported, duplicate, or internally inconsistent
|
|
59
|
+
inventory remains a hard health failure. When inventory is valid, policy ids it
|
|
60
|
+
does not observe are reported as sorted stale-policy observations without
|
|
61
|
+
failing strict health, changing passed mode, or blocking other observed skills.
|
|
62
|
+
The absent skill remains unavailable to route and guard. Stale policy is retained
|
|
63
|
+
until an explicit, confirmation-required forget action removes only that policy
|
|
64
|
+
entry; SkillBoard never auto-deletes it.
|
|
65
|
+
|
|
57
66
|
## Installation default
|
|
58
67
|
|
|
59
68
|
A newly discovered valid skill receives `enabled: true` and `shared: false`.
|
package/docs/reference.md
CHANGED
|
@@ -103,6 +103,17 @@ provenance, digests, aliases, install units, and runtime-component observations.
|
|
|
103
103
|
Only `installed_on` participates in the presence check. Other observations are
|
|
104
104
|
optional audit metadata and never determine availability.
|
|
105
105
|
|
|
106
|
+
Missing, invalid JSON, unsupported flags or format, duplicate records, and
|
|
107
|
+
malformed records are generated-inventory integrity failures. With an otherwise
|
|
108
|
+
valid inventory, policy ids absent from inventory are instead sorted,
|
|
109
|
+
non-blocking stale-policy observations. Doctor/status return them in
|
|
110
|
+
`inventory.stalePolicySkills`; brief returns them in
|
|
111
|
+
`health.inventory.stale_policy_skills`. They do not fail strict health or stop
|
|
112
|
+
observed skills from routing and guard, while the specifically absent skill
|
|
113
|
+
remains unavailable. No command automatically removes stale policy. A current
|
|
114
|
+
`v2:forget-skill:<skill-id>` action requires explicit confirmation, refuses
|
|
115
|
+
installed or shared skills, and changes policy only.
|
|
116
|
+
|
|
106
117
|
Runtime and action authorization are outside SkillBoard's scope. The active
|
|
107
118
|
agent or harness owns permission checks for hooks, MCP servers, commands,
|
|
108
119
|
network access, external writes, destructive operations, and secrets.
|
package/package.json
CHANGED
package/src/advisor/schema.mjs
CHANGED
|
@@ -93,7 +93,8 @@ export function healthFromDoctor(doctor, paths) {
|
|
|
93
93
|
required: true,
|
|
94
94
|
ok: doctor.inventory.ok,
|
|
95
95
|
path: doctor.inventory.path,
|
|
96
|
-
errors: sortedStrings(doctor.inventory.errors)
|
|
96
|
+
errors: sortedStrings(doctor.inventory.errors),
|
|
97
|
+
stale_policy_skills: sortedStrings(doctor.inventory.stalePolicySkills ?? [])
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
100
|
return health;
|
package/src/brief-renderer.mjs
CHANGED
|
@@ -53,6 +53,7 @@ export function renderSkillBrief(brief, options = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
emitIntentRoute(lines, brief);
|
|
55
55
|
emitPolicyHealth(lines, brief);
|
|
56
|
+
emitStalePolicy(lines, brief);
|
|
56
57
|
emitCategorySummary(lines, brief);
|
|
57
58
|
emitNextAction(lines, brief, { verbose });
|
|
58
59
|
emitAvailableNowSection(lines, brief, { verbose });
|
|
@@ -109,6 +110,19 @@ function emitPolicyHealth(lines, brief) {
|
|
|
109
110
|
lines.push("");
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
function emitStalePolicy(lines, brief) {
|
|
114
|
+
const skills = brief.health?.inventory?.stale_policy_skills ?? [];
|
|
115
|
+
if (skills.length === 0) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
lines.push("## Stale removed-skill policy", "");
|
|
119
|
+
lines.push("These policy entries are retained for explicit review and do not block other installed skills.");
|
|
120
|
+
for (const skill of skills) {
|
|
121
|
+
lines.push(`- ${safeText(skill)}`);
|
|
122
|
+
}
|
|
123
|
+
lines.push("");
|
|
124
|
+
}
|
|
125
|
+
|
|
112
126
|
function emitPolicyDiagnostics(lines, label, diagnostics) {
|
|
113
127
|
if (diagnostics.length === 0) {
|
|
114
128
|
return;
|
package/src/cli.mjs
CHANGED
|
@@ -1709,6 +1709,8 @@ function renderDoctorSummary(result) {
|
|
|
1709
1709
|
`SkillBoard doctor: ${status}`,
|
|
1710
1710
|
renderInstallation(result.installation),
|
|
1711
1711
|
`Workspace: ${result.workspace.skills.declared} skills, ${result.workspace.workflows} workflows, ${result.workspace.harnesses} harnesses, ${result.workspace.installUnits.total} install units`,
|
|
1712
|
+
`Inventory integrity: ${result.inventory.ok ? "passed" : "failed"} (${result.inventory.errors.length} errors)`,
|
|
1713
|
+
`Stale removed-skill policy: ${result.inventory.stalePolicySkills.length}`,
|
|
1712
1714
|
`Source audit: ${result.sources.ok ? "passed" : "failed"} (${result.sources.errors.length} errors, ${result.sources.warnings.length} warnings, ${result.sources.blockingWarnings.length} blocking)`,
|
|
1713
1715
|
`Policy: ${result.policy.ok ? "passed" : "failed"} (${result.policy.errors.length} errors, ${result.policy.warnings.length} warnings)`
|
|
1714
1716
|
];
|
|
@@ -1746,6 +1748,8 @@ function renderDoctor(result) {
|
|
|
1746
1748
|
`Config: ${result.config.exists ? result.config.valid ? `valid v${result.config.version}` : `invalid (${result.config.error})` : "missing"}`,
|
|
1747
1749
|
`Bridge: ${bridges}`,
|
|
1748
1750
|
`Workspace: ${result.workspace.skills.declared} declared skills, ${result.workspace.skills.installed} installed skills, ${result.workspace.workflows} workflows, ${result.workspace.harnesses} harnesses, ${result.workspace.installUnits.total} install units`,
|
|
1751
|
+
`Inventory integrity: ${result.inventory.ok ? "passed" : "failed"} (${result.inventory.errors.length} errors)`,
|
|
1752
|
+
`Stale removed-skill policy (${result.inventory.stalePolicySkills.length}): ${formatList(result.inventory.stalePolicySkills)}`,
|
|
1749
1753
|
`Skill states: ${renderCounts(result.workspace.skills.byStatus)}`,
|
|
1750
1754
|
`Invocations: ${renderCounts(result.workspace.skills.byInvocation)}`,
|
|
1751
1755
|
`Source classes: ${renderCounts(result.workspace.installUnits.bySourceClass)}`,
|
package/src/doctor.mjs
CHANGED
|
@@ -47,7 +47,7 @@ export async function doctorProject(options = {}) {
|
|
|
47
47
|
bridges,
|
|
48
48
|
installation,
|
|
49
49
|
workspace: emptyWorkspaceSummary(),
|
|
50
|
-
inventory: { required: false, ok: true, path: null, errors: [] },
|
|
50
|
+
inventory: { required: false, ok: true, path: null, errors: [], stalePolicySkills: [] },
|
|
51
51
|
policy: { ok: false, errors: [], warnings: [] },
|
|
52
52
|
sources: { checked: false, verified: options.verifySources === true, ok: false, errors: [], warnings: [], blockingWarnings: [], units: [] },
|
|
53
53
|
uninstall,
|
|
@@ -136,12 +136,9 @@ function doctorRecommendations(result) {
|
|
|
136
136
|
const recommendations = [];
|
|
137
137
|
if (!result.inventory.ok) {
|
|
138
138
|
recommendations.push("run skillboard inventory refresh and fix generated inventory integrity errors");
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
recommendations.push(`reinstall ${match.groups.skill} or run skillboard skill forget ${match.groups.skill}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
139
|
+
}
|
|
140
|
+
for (const skill of result.inventory.stalePolicySkills) {
|
|
141
|
+
recommendations.push(`reinstall ${skill} or run skillboard skill forget ${skill}`);
|
|
145
142
|
}
|
|
146
143
|
if (!result.bridges.some((bridge) => bridge.status === "installed")) {
|
|
147
144
|
recommendations.push("legacy project bridge blocks are absent; run skillboard init only if maintaining deprecated project-local policy");
|
|
@@ -176,20 +173,22 @@ function doctorRecommendations(result) {
|
|
|
176
173
|
|
|
177
174
|
function inventoryHealth(workspace) {
|
|
178
175
|
if (workspace.version !== 2) {
|
|
179
|
-
return { required: false, ok: true, path: null, errors: [] };
|
|
176
|
+
return { required: false, ok: true, path: null, errors: [], stalePolicySkills: [] };
|
|
180
177
|
}
|
|
181
178
|
const errors = [...(workspace.inventory?.integrityErrors ?? ["generated inventory is unavailable"])];
|
|
182
179
|
const observed = new Set(workspace.inventory?.skillIds ?? []);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
180
|
+
const stalePolicySkills = errors.length === 0
|
|
181
|
+
? workspace.skills
|
|
182
|
+
.map((skill) => skill.id)
|
|
183
|
+
.filter((skillId) => !observed.has(skillId))
|
|
184
|
+
.sort((left, right) => left.localeCompare(right))
|
|
185
|
+
: [];
|
|
188
186
|
return {
|
|
189
187
|
required: true,
|
|
190
188
|
ok: errors.length === 0,
|
|
191
189
|
path: workspace.inventory?.path ?? null,
|
|
192
|
-
errors
|
|
190
|
+
errors,
|
|
191
|
+
stalePolicySkills
|
|
193
192
|
};
|
|
194
193
|
}
|
|
195
194
|
|