agent-skillboard 0.2.2 → 0.2.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 CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.4 — 2026-07-03
6
+
7
+ ### Changed
8
+
9
+ - `skillboard uninstall --purge` now removes the entire `.skillboard/` project
10
+ state directory, including source caches, rollout logs, variant snapshots, and
11
+ profiles, while preserving local skill files.
12
+
13
+ ## 0.2.3 — 2026-07-03
14
+
15
+ ### Added
16
+
17
+ - `skillboard uninstall --purge` removes SkillBoard config, reports, hooks,
18
+ bridge blocks, and empty generated directories while preserving local skill
19
+ files.
20
+
21
+ ### Changed
22
+
23
+ - Cleanup action cards now present the shorter purge command instead of the
24
+ three-flag reset form.
25
+ - Uninstall help and docs clarify the difference between conservative bridge
26
+ removal and full SkillBoard policy-footprint removal.
27
+
5
28
  ## 0.2.2 — 2026-07-03
6
29
 
7
30
  ### Changed
package/README.md CHANGED
@@ -13,7 +13,8 @@ The burden stays low:
13
13
  - Most use is read-only: `brief`, `route`, `doctor`, and `guard use` answer
14
14
  what is safe now.
15
15
  - Nothing changes until you approve a policy action.
16
- - Project cleanup is conservative and previewable with `skillboard uninstall --dry-run`.
16
+ - Project cleanup is conservative and previewable with `skillboard uninstall --dry-run`;
17
+ `--purge` removes SkillBoard's policy footprint while preserving local skills.
17
18
 
18
19
  Status: public alpha. The current config schema is config schema v1; breaking
19
20
  changes may still happen before `1.0.0` and are documented in release notes.
@@ -117,6 +118,17 @@ npx --yes --package agent-skillboard skillboard uninstall --dir /path/to/your/pr
117
118
  Uninstall preserves local skills and policy files by default, and reports what
118
119
  it removed or preserved.
119
120
 
121
+ Remove SkillBoard's policy influence entirely while keeping local skill files:
122
+
123
+ ```bash
124
+ npx --yes --package agent-skillboard skillboard uninstall --dir /path/to/your/project --purge --dry-run
125
+ npx --yes --package agent-skillboard skillboard uninstall --dir /path/to/your/project --purge
126
+ ```
127
+
128
+ `--purge` deletes SkillBoard config, bridge blocks, and the entire
129
+ `.skillboard/` project state directory while leaving `skills/*/SKILL.md` in
130
+ place.
131
+
120
132
  See [docs/install.md](docs/install.md) for global installs, `--dir`, GitHub
121
133
  builds, clone-based development, Hermes bridge setup, refresh, and uninstall.
122
134
 
package/docs/install.md CHANGED
@@ -311,6 +311,20 @@ Default uninstall behavior is conservative:
311
311
  hooks, and modified files by default. Empty generated directories can be
312
312
  removed.
313
313
 
314
+ Use `--purge` when you want SkillBoard's influence removed from the project
315
+ instead of merely disconnecting the bridge:
316
+
317
+ ```bash
318
+ skillboard uninstall --dir /path/to/your/project --purge --dry-run
319
+ skillboard uninstall --dir /path/to/your/project --purge
320
+ ```
321
+
322
+ `--purge` removes SkillBoard config, bridge blocks, and the entire
323
+ `.skillboard/` project state directory, including reports, hooks, source caches,
324
+ rollout logs, variant snapshots, and profiles. It preserves local `skills/`
325
+ files because skills that were created or deleted are outside the uninstall
326
+ scope.
327
+
314
328
  Use `--remove-config` to delete `skillboard.config.yaml` only when it still
315
329
  matches the untouched default config. If the config contains scanned skills or
316
330
  user edits, uninstall preserves it and reports it under `Preserved`.
@@ -330,7 +344,9 @@ Add `--remove-hooks` only when a reset should also delete the entire
330
344
  `.skillboard/hooks/` directory contents. This is explicit because hook scripts
331
345
  may be wired into local agent/runtime configuration. Combine `--reset-config`,
332
346
  `--remove-reports`, and `--remove-hooks` for a clean test reset that removes
333
- SkillBoard-owned lifecycle scaffolding while preserving local `skills/`.
347
+ the most common SkillBoard-owned lifecycle scaffolding while preserving local
348
+ `skills/`. Use `--purge` instead when no `.skillboard/` project state should
349
+ remain at all.
334
350
 
335
351
  ## Upper-Layer Control
336
352
 
package/docs/reference.md CHANGED
@@ -52,7 +52,7 @@ node bin/skillboard.mjs brief --dir /path/to/your/project --workflow <workflow-f
52
52
 
53
53
  ```bash
54
54
  skillboard init [--dir <path>] [--scan-root <dir>[,<dir>]] [--no-scan-installed]
55
- skillboard uninstall [--dir <path>] [--dry-run] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]
55
+ skillboard uninstall [--dir <path>] [--dry-run] [--purge] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]
56
56
  skillboard inventory refresh [--dir <path>] [--config <path>] [--scan-root <dir>[,<dir>]] [--dry-run] [--json]
57
57
  skillboard inventory detect --unit <id> --config <path> [--install-output <path>] [--config-file a,b] [--source <value>] [--kind <kind>] [--scope <scope>] [--dry-run] [--json]
58
58
  skillboard sources refresh [--dir <path>] [--config <path>] [--unit <id>[,<id>]] [--cache-dir <dir>] [--dry-run] [--json]
package/docs/user-flow.md CHANGED
@@ -261,8 +261,8 @@ skillboard remove skill user.helper \
261
261
  --force
262
262
  ```
263
263
 
264
- This removes SkillBoard policy references only. It does not delete
265
- `skills/user-helper/SKILL.md`.
264
+ This removes SkillBoard policy references only and leaves
265
+ `skills/user-helper/SKILL.md` in place.
266
266
 
267
267
  ## 6. Stop Using SkillBoard Safely
268
268
 
@@ -280,14 +280,14 @@ content in bridge files.
280
280
  For a fresh policy lifecycle during testing, use:
281
281
 
282
282
  ```bash
283
- skillboard uninstall --reset-config --remove-reports --remove-hooks --dry-run
284
- skillboard uninstall --reset-config --remove-reports --remove-hooks
283
+ skillboard uninstall --purge --dry-run
284
+ skillboard uninstall --purge
285
285
  skillboard init
286
286
  ```
287
287
 
288
- `--reset-config` discards the current SkillBoard config even if it contains
289
- imported skills or workflow edits. `--remove-reports` also discards generated
290
- dashboard and impact reports. `--remove-hooks` discards the entire
291
- `.skillboard/hooks/` directory contents when you explicitly want a full test or
292
- test reset, so `.skillboard/` can disappear when no other SkillBoard state
293
- remains. These reset flags do not delete local `skills/` files.
288
+ Use `--purge` when you want SkillBoard's allow/block/preference influence gone
289
+ from the project while keeping local skill files. It discards the current
290
+ SkillBoard config even if it contains imported skills or workflow edits,
291
+ removes generated dashboard and impact reports, and removes the entire
292
+ `.skillboard/` project state directory, including hooks, source caches, rollout
293
+ logs, variant snapshots, and profiles. Local `skills/` files stay in place.
@@ -8,7 +8,7 @@ source profiles, and the generated lockfile.
8
8
 
9
9
  ## Status
10
10
 
11
- Current package version: `0.2.2`
11
+ Current package version: `0.2.4`
12
12
 
13
13
  Current config schema version:
14
14
 
@@ -59,8 +59,8 @@ must call them out clearly.
59
59
  Suggested tags:
60
60
 
61
61
  - `v0.1.0-alpha`: first public GitHub alpha.
62
- - `v0.2.2`: one-time runtime source review before manual activation, ask-after
63
- skill routing, README benefit-first positioning,
62
+ - `v0.2.4`: one-time runtime source review before manual activation, ask-after
63
+ skill routing, purge uninstall, README benefit-first positioning,
64
64
  source inventory refresh, doctor/status, source pin refresh,
65
65
  installer/config detection, resilient detector warnings, and richer dry-run
66
66
  plans.
@@ -194,8 +194,8 @@ Before tagging a public release:
194
194
  provenance with GitHub Actions OIDC, so keep `permissions.id-token: write`,
195
195
  the npm registry URL in `setup-node`, and
196
196
  `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` on the publish step.
197
- - Push a version tag that exactly matches `package.json`, for example `v0.2.2`
198
- for package version `0.2.2`.
197
+ - Push a version tag that exactly matches `package.json`, for example `v0.2.4`
198
+ for package version `0.2.4`.
199
199
  - Let `.github/workflows/publish.yml` publish from the tag. The workflow runs
200
200
  the full check suite, validates that the tag matches the package version, and
201
201
  skips `npm publish` only when that exact version already exists on npm.
@@ -229,6 +229,22 @@ completion notes:
229
229
  - update README quick-start to verify the installed version before running
230
230
  policy commands.
231
231
 
232
+ ## 0.2.4 Completion Notes
233
+
234
+ - broaden `skillboard uninstall --purge` from reports/hooks cleanup to removal
235
+ of the full `.skillboard/` project state directory, covering source caches,
236
+ rollout logs, variant snapshots, profiles, hooks, and reports while preserving
237
+ local skill files.
238
+
239
+ ## 0.2.3 Completion Notes
240
+
241
+ - add `skillboard uninstall --purge` for users who want SkillBoard's policy
242
+ influence and generated footprint removed while preserving local skill files;
243
+ - document the lifecycle split between conservative bridge cleanup and full
244
+ SkillBoard policy-footprint removal;
245
+ - show purge in cleanup action cards so users do not need to remember the
246
+ equivalent `--reset-config --remove-reports --remove-hooks` sequence.
247
+
232
248
  ## 0.2.2 Completion Notes
233
249
 
234
250
  - recommend one-time review, not default blocking, for high-risk runtime/plugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-skillboard",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Let AI agents pick and use allowed skills in each workflow.",
5
5
  "keywords": [
6
6
  "ai-agent",
@@ -199,19 +199,17 @@ function removeSkillForceActions({ paths, workflow, skills, workspace }) {
199
199
 
200
200
  function resetCleanupAction({ paths, workflow, cleanup }) {
201
201
  const dryRun = command([
202
- "skillboard", "uninstall", "--dir", paths.root, "--dry-run",
203
- "--reset-config", "--remove-reports", "--remove-hooks"
202
+ "skillboard", "uninstall", "--dir", paths.root, "--purge", "--dry-run"
204
203
  ]);
205
204
  return makeAction({
206
205
  kind: "reset-cleanup",
207
206
  targetId: paths.root,
208
- label: "Reset SkillBoard generated project files",
209
- reason: "Preview full SkillBoard cleanup before applying it.",
207
+ label: "Purge SkillBoard project footprint",
208
+ reason: "Preview full SkillBoard policy cleanup before applying it.",
210
209
  risk: "destructive",
211
210
  dryRun,
212
211
  apply: applyOrNull(workflow, dryRun, [
213
- "skillboard", "uninstall", "--dir", paths.root,
214
- "--reset-config", "--remove-reports", "--remove-hooks"
212
+ "skillboard", "uninstall", "--dir", paths.root, "--purge"
215
213
  ]),
216
214
  appliesTo: { kind: "project", id: paths.root },
217
215
  blockedReason: blockedByWorkflow(workflow),
package/src/cli.mjs CHANGED
@@ -61,7 +61,7 @@ const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.u
61
61
 
62
62
  const APPLY_ACTION_VALUE_OPTIONS = new Set(["workflow", "dir", "config", "skills", "out", "skillboard-bin"]);
63
63
  const COMMAND_USAGE = new Map([
64
- ["uninstall", ["uninstall [--dir <path>] [--dry-run] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]"]],
64
+ ["uninstall", ["uninstall [--dir <path>] [--dry-run] [--purge] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]"]],
65
65
  [
66
66
  "inventory",
67
67
  [
@@ -1501,7 +1501,7 @@ function helpText() {
1501
1501
  "",
1502
1502
  "AI/automation operations:",
1503
1503
  " init [--dir <path>] [--scan-root <dir>[,<dir>]] [--no-scan-installed]",
1504
- " uninstall [--dir <path>] [--dry-run] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]",
1504
+ " uninstall [--dir <path>] [--dry-run] [--purge] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]",
1505
1505
  " inventory refresh [--dir <path>] [--config <path>] [--scan-root <dir>[,<dir>]] [--dry-run] [--json]",
1506
1506
  " inventory detect --unit <id> --config <path> [--install-output <path>] [--config-file a,b] [--source <value>] [--kind <kind>] [--scope <scope>] [--dry-run] [--json]",
1507
1507
  " sources refresh [--dir <path>] [--config <path>] [--unit <id>[,<id>]] [--cache-dir <dir>] [--dry-run] [--json]",
@@ -1560,6 +1560,9 @@ function commandHelpText(command) {
1560
1560
  if (command === "init") {
1561
1561
  return initHelpText();
1562
1562
  }
1563
+ if (command === "uninstall") {
1564
+ return uninstallHelpText();
1565
+ }
1563
1566
  if (command === "doctor" || command === "status") {
1564
1567
  return doctorHelpText();
1565
1568
  }
@@ -1610,6 +1613,33 @@ function initHelpText() {
1610
1613
  ].join("\n");
1611
1614
  }
1612
1615
 
1616
+ function uninstallHelpText() {
1617
+ return [
1618
+ "Usage: skillboard uninstall [--dir <path>] [--dry-run] [--purge] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs]",
1619
+ "",
1620
+ "This help is read-only. It does not load config or change project files.",
1621
+ "",
1622
+ "Removes SkillBoard project bridge files and generated lifecycle scaffolding.",
1623
+ "Default cleanup is conservative and preserves policy, reports, hooks, local skills, and user-authored content.",
1624
+ "",
1625
+ "Options:",
1626
+ " --dir <path> Project root to clean up; defaults to the current directory.",
1627
+ " --dry-run Preview the cleanup without writing changes.",
1628
+ " --purge Remove SkillBoard policy influence and generated footprint while preserving local skills.",
1629
+ " --remove-config Delete skillboard.config.yaml only if it still matches the generated default.",
1630
+ " --reset-config Delete skillboard.config.yaml even when it contains policy choices.",
1631
+ " --remove-reports Delete .skillboard/reports/.",
1632
+ " --remove-hooks Delete .skillboard/hooks/.",
1633
+ " --keep-empty-dirs Preserve empty generated directories.",
1634
+ "",
1635
+ "Purge:",
1636
+ " Removes SkillBoard config, bridge blocks, and the entire .skillboard/ project state directory.",
1637
+ " This includes reports, hooks, source caches, rollout logs, variant snapshots, and profiles.",
1638
+ " It does not delete local skills under skills/.",
1639
+ ""
1640
+ ].join("\n");
1641
+ }
1642
+
1613
1643
  function doctorHelpText() {
1614
1644
  return [
1615
1645
  "Usage: skillboard doctor [--dir <path>] [--config <path>] [--skills <dir>] [--verify] [--strict] [--summary] [--json]",
@@ -37,17 +37,24 @@ export async function runInitCommand(options, stdout, runtime = defaultRuntime()
37
37
  }
38
38
 
39
39
  export async function runUninstallCommand(options, stdout) {
40
- if (options.get("remove-config") === "true" && options.get("reset-config") === "true") {
40
+ const removeConfig = options.get("remove-config") === "true";
41
+ const resetConfig = options.get("reset-config") === "true";
42
+ const purge = options.get("purge") === "true";
43
+ if (removeConfig && resetConfig) {
41
44
  throw new Error("--remove-config and --reset-config are mutually exclusive");
42
45
  }
46
+ if (removeConfig && purge) {
47
+ throw new Error("--remove-config cannot be combined with --purge");
48
+ }
43
49
  const root = resolve(options.get("dir") ?? ".");
44
50
  const result = await uninstallProject({
45
51
  root,
46
52
  dryRun: options.get("dry-run") === "true",
47
- removeConfig: options.get("remove-config") === "true",
48
- resetConfig: options.get("reset-config") === "true",
49
- removeReports: options.get("remove-reports") === "true",
50
- removeHooks: options.get("remove-hooks") === "true",
53
+ removeConfig,
54
+ resetConfig: resetConfig || purge,
55
+ removeReports: options.get("remove-reports") === "true" || purge,
56
+ removeHooks: options.get("remove-hooks") === "true" || purge,
57
+ removeProjectState: purge,
51
58
  removeEmptyDirs: options.get("keep-empty-dirs") !== "true"
52
59
  });
53
60
  stdout.write(`${result.dryRun ? "Dry run: " : ""}Uninstalled SkillBoard: ${root}\n`);
package/src/uninstall.mjs CHANGED
@@ -19,14 +19,6 @@ export async function uninstallProject(options) {
19
19
  recordFileResult(filename, result, { removed, updated, preserved });
20
20
  }
21
21
 
22
- for (const entry of generatedFiles(root)) {
23
- const result = await removeGeneratedFile(entry.path, entry.expected, dryRun);
24
- if (result === "removed") {
25
- plannedRemovedPaths.add(entry.path);
26
- }
27
- recordFileResult(entry.label, result, { removed, updated, preserved });
28
- }
29
-
30
22
  if (options.resetConfig === true) {
31
23
  const path = join(root, "skillboard.config.yaml");
32
24
  const result = await removeConfigFile(path, dryRun);
@@ -45,7 +37,24 @@ export async function uninstallProject(options) {
45
37
  preserved.push("skillboard.config.yaml");
46
38
  }
47
39
 
48
- if (options.removeReports === true) {
40
+ if (options.removeProjectState === true) {
41
+ const path = join(root, ".skillboard");
42
+ const result = await removeProjectStateDir(path, dryRun);
43
+ if (result === "removed") {
44
+ plannedRemovedPaths.add(path);
45
+ }
46
+ recordFileResult(".skillboard", result, { removed, updated, preserved });
47
+ } else {
48
+ for (const entry of generatedFiles(root)) {
49
+ const result = await removeGeneratedFile(entry.path, entry.expected, dryRun);
50
+ if (result === "removed") {
51
+ plannedRemovedPaths.add(entry.path);
52
+ }
53
+ recordFileResult(entry.label, result, { removed, updated, preserved });
54
+ }
55
+ }
56
+
57
+ if (options.removeReports === true && options.removeProjectState !== true) {
49
58
  const path = join(root, ".skillboard", "reports");
50
59
  const result = await removeGeneratedDir(path, dryRun);
51
60
  if (result === "removed") {
@@ -54,7 +63,7 @@ export async function uninstallProject(options) {
54
63
  recordFileResult(".skillboard/reports", result, { removed, updated, preserved });
55
64
  }
56
65
 
57
- if (options.removeHooks === true) {
66
+ if (options.removeHooks === true && options.removeProjectState !== true) {
58
67
  const path = join(root, ".skillboard", "hooks");
59
68
  const result = await removeGeneratedDir(path, dryRun);
60
69
  if (result === "removed") {
@@ -66,7 +75,8 @@ export async function uninstallProject(options) {
66
75
  if (options.removeEmptyDirs !== false) {
67
76
  for (const dir of emptyDirs(root, {
68
77
  skipReports: options.removeReports === true,
69
- skipHooks: options.removeHooks === true
78
+ skipHooks: options.removeHooks === true,
79
+ skipSkillboard: options.removeProjectState === true
70
80
  })) {
71
81
  const result = await removeEmptyDir(dir.path, dryRun, plannedRemovedPaths);
72
82
  if (result === "removed") {
@@ -109,6 +119,9 @@ function emptyDirs(root, options = {}) {
109
119
  if (options.skipHooks === true && dir.label === ".skillboard/hooks") {
110
120
  return false;
111
121
  }
122
+ if (options.skipSkillboard === true && dir.label.startsWith(".skillboard")) {
123
+ return false;
124
+ }
112
125
  return true;
113
126
  });
114
127
  }
@@ -210,6 +223,26 @@ async function removeGeneratedDir(path, dryRun) {
210
223
  return "removed";
211
224
  }
212
225
 
226
+ async function removeProjectStateDir(path, dryRun) {
227
+ const stats = await pathStats(path);
228
+ if (stats === null) {
229
+ return "absent";
230
+ }
231
+ if (stats.isSymbolicLink()) {
232
+ if (!dryRun) {
233
+ await rm(path);
234
+ }
235
+ return "removed";
236
+ }
237
+ if (!stats.isDirectory()) {
238
+ return "preserved";
239
+ }
240
+ if (!dryRun) {
241
+ await rm(path, { recursive: true });
242
+ }
243
+ return "removed";
244
+ }
245
+
213
246
  async function removeEmptyDir(path, dryRun, plannedRemovedPaths) {
214
247
  const stats = await pathStats(path);
215
248
  if (stats === null) {