agent-skillboard 0.1.2 → 0.2.2

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +159 -630
  3. package/docs/adapters.md +96 -96
  4. package/docs/ai-skill-routing-goal.md +112 -0
  5. package/docs/capabilities.md +6 -0
  6. package/docs/install.md +172 -114
  7. package/docs/policy-model.md +266 -214
  8. package/docs/positioning.md +94 -94
  9. package/docs/reference.md +349 -0
  10. package/docs/routing.md +85 -0
  11. package/docs/user-flow.md +79 -17
  12. package/docs/value-proof.md +194 -0
  13. package/docs/variant-lifecycle.md +86 -0
  14. package/docs/versioning.md +154 -138
  15. package/examples/multi-source-skills/anthropic/docx/SKILL.md +8 -8
  16. package/examples/multi-source-skills/anthropic/skill-creator/SKILL.md +8 -8
  17. package/examples/multi-source-skills/matt/grill-me/SKILL.md +8 -8
  18. package/examples/multi-source-skills/matt/tdd/SKILL.md +8 -8
  19. package/examples/multi-source-skills/omo/review-work/SKILL.md +8 -8
  20. package/examples/multi-source-skills/omo/ulw-plan/SKILL.md +8 -8
  21. package/examples/multi-source-skills/private/tdd-work-continuity/SKILL.md +8 -8
  22. package/examples/multi-source-skills/private/workflow-router/SKILL.md +8 -8
  23. package/examples/multi-source-skills/wshobson/python-testing/SKILL.md +8 -8
  24. package/examples/multi-source-skills/wshobson/security-review/SKILL.md +8 -8
  25. package/examples/skillboard.config.yaml +8 -3
  26. package/examples/skills/grill-me/SKILL.md +9 -9
  27. package/examples/skills/grill-with-docs/SKILL.md +9 -9
  28. package/examples/skills/requirement-intake/SKILL.md +9 -9
  29. package/examples/skills/tdd/SKILL.md +8 -8
  30. package/package.json +6 -3
  31. package/src/advisor/actions.mjs +25 -3
  32. package/src/advisor/guidance.mjs +232 -0
  33. package/src/advisor/schema.mjs +2 -0
  34. package/src/advisor/skills.mjs +2 -0
  35. package/src/advisor/trust-policy.mjs +5 -3
  36. package/src/advisor.mjs +36 -7
  37. package/src/brief-cli.mjs +6 -5
  38. package/src/brief-renderer.mjs +225 -8
  39. package/src/cli.mjs +574 -27
  40. package/src/config-helpers.mjs +34 -18
  41. package/src/conflicts.mjs +70 -0
  42. package/src/control/can-use-guard.mjs +8 -3
  43. package/src/control/skill-crud.mjs +142 -0
  44. package/src/control/skill-variants.mjs +221 -0
  45. package/src/control/source-trust.mjs +1 -0
  46. package/src/control/variant-files.mjs +265 -0
  47. package/src/control/variant-lifecycle-config.mjs +156 -0
  48. package/src/control/variant-reset.mjs +171 -0
  49. package/src/control/variant-status.mjs +75 -0
  50. package/src/control.mjs +13 -1
  51. package/src/domain/rules/skills.mjs +60 -0
  52. package/src/domain/rules/workflows.mjs +13 -0
  53. package/src/hook-plan.mjs +6 -6
  54. package/src/impact.mjs +21 -12
  55. package/src/index.mjs +13 -1
  56. package/src/lifecycle-cli.mjs +19 -8
  57. package/src/lifecycle-content.mjs +34 -24
  58. package/src/route.mjs +537 -0
  59. package/src/source-verification.mjs +7 -3
  60. package/src/workspace.mjs +141 -43
  61. package/tsconfig.lsp.json +1 -1
  62. package/docs/plans/20260625-080025-skillboard-mvp-review.md +0 -189
  63. package/docs/plans/README.md +0 -20
package/src/control.mjs CHANGED
@@ -14,11 +14,18 @@ import {
14
14
  import {
15
15
  activateSkill,
16
16
  addSkill,
17
+ addSkillVariant,
17
18
  blockSkill,
18
19
  preferSkill,
19
20
  quarantineSkill,
20
21
  removeSkill
21
22
  } from "./control/skill-crud.mjs";
23
+ import {
24
+ approveSkillVariant,
25
+ forkSkillVariant
26
+ } from "./control/skill-variants.mjs";
27
+ import { resetSkillVariant } from "./control/variant-reset.mjs";
28
+ import { variantLifecycleStatus } from "./control/variant-status.mjs";
22
29
  import { addHarness, addWorkflow } from "./control/workflow-crud.mjs";
23
30
  import {
24
31
  GUARD_HOOK_MODE,
@@ -116,15 +123,20 @@ export {
116
123
  activateSkill,
117
124
  addHarness,
118
125
  addSkill,
126
+ addSkillVariant,
119
127
  addWorkflow,
128
+ approveSkillVariant,
120
129
  auditSources,
121
130
  blockSkill,
122
131
  canUseSkill,
123
132
  classifySkillSource,
124
133
  classifySkillTrust,
134
+ forkSkillVariant,
125
135
  preferSkill,
126
136
  quarantineSkill,
127
- removeSkill
137
+ removeSkill,
138
+ resetSkillVariant,
139
+ variantLifecycleStatus
128
140
  };
129
141
 
130
142
  export async function installGuardHook(options) {
@@ -4,6 +4,10 @@ import {
4
4
  validateSkillState
5
5
  } from "../skill-state-matrix.mjs";
6
6
 
7
+ const VARIANT_STATUS_VALUES = new Set(["draft", "approved"]);
8
+ const VARIANT_DIGEST_PATTERN = /^sha256:[0-9a-fA-F]{64}$/;
9
+ const VARIANT_SNAPSHOT_PREFIX = ".skillboard/variant-snapshots/";
10
+
7
11
  export const skillRules = [
8
12
  {
9
13
  id: "SKILL-STATUS-001",
@@ -71,6 +75,19 @@ export const skillRules = [
71
75
  return diagnostics;
72
76
  }
73
77
  },
78
+ {
79
+ id: "SKILL-VARIANT-001",
80
+ check(ctx) {
81
+ const diagnostics = [];
82
+ for (const skill of ctx.skills) {
83
+ if (skill.variant === null || skill.variant === undefined) {
84
+ continue;
85
+ }
86
+ diagnostics.push(...validateVariantMetadata(ctx, skill));
87
+ }
88
+ return diagnostics;
89
+ }
90
+ },
74
91
  {
75
92
  id: "SKILL-OWNER-001",
76
93
  check(ctx) {
@@ -103,3 +120,46 @@ function validateStatusInvocation(skill) {
103
120
  }
104
121
  return diagnostics;
105
122
  }
123
+
124
+ function validateVariantMetadata(ctx, skill) {
125
+ const diagnostics = [];
126
+ const { variant } = skill;
127
+ if (!ctx.skillsById.has(variant.of)) {
128
+ diagnostics.push(`Skill ${skill.id} variant.of references undeclared skill: ${variant.of}`);
129
+ }
130
+ if (!ctx.capabilitiesByName.has(variant.capability)) {
131
+ diagnostics.push(`Skill ${skill.id} variant.capability references undeclared capability: ${variant.capability}`);
132
+ }
133
+ if (!ctx.workflowsByName.has(variant.workflow)) {
134
+ diagnostics.push(`Skill ${skill.id} variant.workflow references undeclared workflow: ${variant.workflow}`);
135
+ }
136
+ if (!VARIANT_STATUS_VALUES.has(variant.status)) {
137
+ diagnostics.push(`Skill ${skill.id} variant.status must be one of: draft, approved; got ${variant.status}.`);
138
+ }
139
+ diagnostics.push(...validateVariantCheckpoint(skill, "base", variant.base));
140
+ if (variant.approved !== undefined) {
141
+ diagnostics.push(...validateVariantCheckpoint(skill, "approved", variant.approved));
142
+ }
143
+ return diagnostics;
144
+ }
145
+
146
+ function validateVariantCheckpoint(skill, key, checkpoint) {
147
+ const diagnostics = [];
148
+ if (!VARIANT_DIGEST_PATTERN.test(checkpoint.contentDigest)) {
149
+ diagnostics.push(`Skill ${skill.id} variant.${key}.content_digest must match sha256:<64 hex chars>.`);
150
+ }
151
+ if (!isVariantSnapshotPath(checkpoint.snapshot)) {
152
+ diagnostics.push(`Skill ${skill.id} variant.${key}.snapshot must be a relative path under ${VARIANT_SNAPSHOT_PREFIX}.`);
153
+ }
154
+ return diagnostics;
155
+ }
156
+
157
+ function isVariantSnapshotPath(value) {
158
+ if (value.startsWith("/") || /^[A-Za-z]:/.test(value) || value.includes("\\")) {
159
+ return false;
160
+ }
161
+ if (!value.startsWith(VARIANT_SNAPSHOT_PREFIX) || value.length <= VARIANT_SNAPSHOT_PREFIX.length) {
162
+ return false;
163
+ }
164
+ return value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
165
+ }
@@ -3,6 +3,7 @@ import {
3
3
  NON_CALLABLE_WORKFLOW_INVOCATIONS,
4
4
  NON_CALLABLE_WORKFLOW_STATUSES
5
5
  } from "../constants.mjs";
6
+ import { workflowConflictEntries } from "../../conflicts.mjs";
6
7
 
7
8
  export const workflowRules = [
8
9
  {
@@ -90,6 +91,18 @@ export const workflowRules = [
90
91
  }
91
92
  return diagnostics;
92
93
  }
94
+ },
95
+ {
96
+ id: "WF-CONFLICT-001",
97
+ check(ctx) {
98
+ const diagnostics = [];
99
+ for (const workflow of ctx.workflows) {
100
+ for (const conflict of workflowConflictEntries(ctx.workspace, workflow)) {
101
+ diagnostics.push(`Workflow ${workflow.name} has active skill conflict: ${conflict.skill} conflicts_with ${conflict.conflictingSkill}.`);
102
+ }
103
+ }
104
+ return diagnostics;
105
+ }
93
106
  }
94
107
  ];
95
108
 
package/src/hook-plan.mjs CHANGED
@@ -58,10 +58,10 @@ function renderGuardHookScript(options) {
58
58
  return `#!/usr/bin/env sh
59
59
  set -eu
60
60
 
61
- SKILLBOARD_BIN=\${SKILLBOARD_BIN:-${shellQuote(options.command)}}
62
- SKILLBOARD_CONFIG=\${SKILLBOARD_CONFIG:-${shellQuote(options.configPath)}}
63
- SKILLBOARD_SKILLS=\${SKILLBOARD_SKILLS:-${shellQuote(options.skillsRoot)}}
64
- SKILLBOARD_WORKFLOW=\${SKILLBOARD_WORKFLOW:-${shellQuote(options.workflow)}}
61
+ SKILLBOARD_BIN=${shellQuote(options.command)}
62
+ SKILLBOARD_CONFIG=${shellQuote(options.configPath)}
63
+ SKILLBOARD_SKILLS=${shellQuote(options.skillsRoot)}
64
+ SKILLBOARD_WORKFLOW=${shellQuote(options.workflow)}
65
65
 
66
66
  if [ "\${SKILLBOARD_SKILL_ID:-}" != "" ]; then
67
67
  skill_id="$SKILLBOARD_SKILL_ID"
@@ -72,8 +72,8 @@ else
72
72
  exit 64
73
73
  fi
74
74
 
75
- # Split SKILLBOARD_BIN so local hook configs can use commands like:
76
- # SKILLBOARD_BIN="node bin/skillboard.mjs"
75
+ # Split the install-time command so hooks can use commands like:
76
+ # --skillboard-bin "node bin/skillboard.mjs"
77
77
  # Paths containing spaces should be provided through an environment wrapper.
78
78
  set -- $SKILLBOARD_BIN
79
79
  exec "$@" guard use "$skill_id" --workflow "$SKILLBOARD_WORKFLOW" --config "$SKILLBOARD_CONFIG" --skills "$SKILLBOARD_SKILLS"
package/src/impact.mjs CHANGED
@@ -1,21 +1,30 @@
1
- export function impactDisable(workspace, skillId) {
1
+ import {
2
+ activeConflictEntriesForSkill,
3
+ conflictingSkillIds
4
+ } from "./conflicts.mjs";
5
+
6
+ export function impactDisable(workspace, skillId) {
2
7
  const affectedWorkflowEntries = workspace.workflows
3
8
  .filter((workflow) => workflow.activeSkills.includes(skillId) || workflow.requiredCapabilities.some((capability) => {
4
9
  return capability.preferred === skillId || capability.fallback.includes(skillId);
5
10
  }));
6
11
  const affectedWorkflows = affectedWorkflowEntries.map((workflow) => workflow.name);
7
12
  const affectedOutputs = [...new Set(affectedWorkflowEntries.flatMap((workflow) => workflow.requiredOutputs))];
8
- const skill = workspace.skills.find((candidate) => candidate.id === skillId);
9
- const alternatives = alternativesForSkill(workspace, skillId, skill);
10
- return {
11
- skillId,
12
- exists: skill !== undefined,
13
- affectedWorkflows,
14
- affectedOutputs,
15
- alternatives,
16
- risk: riskFor(skill, affectedWorkflows, alternatives)
17
- };
18
- }
13
+ const skill = workspace.skills.find((candidate) => candidate.id === skillId);
14
+ const alternatives = alternativesForSkill(workspace, skillId, skill);
15
+ const conflictingSkills = conflictingSkillIds(workspace, skillId);
16
+ const activeConflicts = activeConflictEntriesForSkill(workspace, skillId);
17
+ return {
18
+ skillId,
19
+ exists: skill !== undefined,
20
+ affectedWorkflows,
21
+ affectedOutputs,
22
+ alternatives,
23
+ conflictingSkills,
24
+ activeConflicts,
25
+ risk: riskFor(skill, affectedWorkflows, alternatives)
26
+ };
27
+ }
19
28
 
20
29
  function alternativesForSkill(workspace, skillId, skill) {
21
30
  if (skill?.replacedBy !== undefined) {
package/src/index.mjs CHANGED
@@ -15,15 +15,19 @@ import { detectInstallOutput } from "./install-output-detector.mjs";
15
15
  import { rolloutApply, rolloutAudit, rolloutPlan, rolloutReport, rolloutRollback } from "./rollout.mjs";
16
16
  import { reviewInstallUnit } from "./review.mjs";
17
17
  import { buildSkillBrief } from "./advisor.mjs";
18
+ import { routeSkill } from "./route.mjs";
18
19
  import {
19
20
  activateSkill,
20
21
  addHarness,
21
22
  addSkill,
23
+ addSkillVariant,
22
24
  addWorkflow,
25
+ approveSkillVariant,
23
26
  auditSources,
24
27
  blockSkill,
25
28
  canUseSkill,
26
29
  explainSkill,
30
+ forkSkillVariant,
27
31
  installGuardHook,
28
32
  listHarnesses,
29
33
  listInstallUnits,
@@ -31,14 +35,18 @@ import {
31
35
  listWorkflows,
32
36
  preferSkill,
33
37
  quarantineSkill,
34
- removeSkill
38
+ removeSkill,
39
+ resetSkillVariant,
40
+ variantLifecycleStatus
35
41
  } from "./control.mjs";
36
42
 
37
43
  export {
38
44
  activateSkill,
39
45
  addHarness,
40
46
  addSkill,
47
+ addSkillVariant,
41
48
  addWorkflow,
49
+ approveSkillVariant,
42
50
  agentInventoryDetectors,
43
51
  auditSources,
44
52
  blockSkill,
@@ -49,6 +57,7 @@ export {
49
57
  detectInstallOutput,
50
58
  doctorProject,
51
59
  explainSkill,
60
+ forkSkillVariant,
52
61
  impactDisable,
53
62
  importSource,
54
63
  initProject,
@@ -64,6 +73,7 @@ export {
64
73
  preferSkill,
65
74
  quarantineSkill,
66
75
  removeSkill,
76
+ resetSkillVariant,
67
77
  reconcileWorkspace,
68
78
  refreshAgentInventory,
69
79
  refreshSourcePins,
@@ -74,9 +84,11 @@ export {
74
84
  rolloutPlan,
75
85
  rolloutReport,
76
86
  rolloutRollback,
87
+ routeSkill,
77
88
  renderImportFragment,
78
89
  renderReconcilePlan,
79
90
  uninstallProject,
80
91
  verifySources,
92
+ variantLifecycleStatus,
81
93
  writeLockfile
82
94
  };
@@ -30,7 +30,8 @@ export async function runInitCommand(options, stdout, runtime = defaultRuntime()
30
30
  writeSafetyDefault(stdout, result.safety);
31
31
  writeNextCommands(stdout, {
32
32
  command: commandPrefix(runtime),
33
- dir: options.get("dir")
33
+ dir: options.get("dir"),
34
+ workflows: result.scan.addedWorkflows ?? []
34
35
  });
35
36
  return 0;
36
37
  }
@@ -85,7 +86,7 @@ function writeSafetyDefault(stdout, safety) {
85
86
  stdout.write(`- ${safety.automatic} automatic skills enabled by existing policy.\n`);
86
87
  }
87
88
  stdout.write("- Imported local skills are manual-only.\n");
88
- stdout.write("- Runtime/plugin/system skills are quarantined until reviewed.\n");
89
+ stdout.write("- Runtime/plugin/system skills require source review before manual activation.\n");
89
90
  stdout.write(`- ${safety.automatic} automatic skills enabled\n`);
90
91
  stdout.write(`- ${safety.manualOnly} manual-only skills available\n`);
91
92
  stdout.write(`- ${safety.routerOnly} router-only skills available\n`);
@@ -94,10 +95,22 @@ function writeSafetyDefault(stdout, safety) {
94
95
 
95
96
  function writeNextCommands(stdout, next) {
96
97
  const dir = next.dir === undefined ? "" : ` --dir ${shellQuote(next.dir)}`;
98
+ const workflows = sorted(next.workflows ?? []);
97
99
  stdout.write("Next:\n");
100
+ stdout.write('- Ask your AI: "What skills can you use in this project?"\n');
98
101
  stdout.write(`- ${next.command} doctor${dir} --summary\n`);
99
- stdout.write(`- ${next.command} brief${dir}\n`);
100
- stdout.write(`- ${next.command} brief${dir} --verbose\n`);
102
+ if (workflows.length === 0) {
103
+ stdout.write(`- ${next.command} brief${dir}\n`);
104
+ stdout.write(`- ${next.command} brief${dir} --verbose\n`);
105
+ return;
106
+ }
107
+ const workflow = workflows[0];
108
+ stdout.write(`Choose a workflow: ${formatList(workflows)}\n`);
109
+ stdout.write(`Example workflow brief: ${formatList([workflow])}\n`);
110
+ stdout.write(`- ${next.command} brief --workflow ${shellQuote(workflow)}${dir}\n`);
111
+ stdout.write('Example task routing: "write tests before implementation"\n');
112
+ stdout.write(`- ${next.command} brief --workflow ${shellQuote(workflow)} --intent ${shellQuote("write tests before implementation")}${dir}\n`);
113
+ stdout.write(`- ${next.command} brief --workflow ${shellQuote(workflow)}${dir} --verbose\n`);
101
114
  }
102
115
 
103
116
  function formatList(values) {
@@ -127,10 +140,8 @@ function commandPrefix(runtime) {
127
140
  const entrypoint = runtime.entrypointPath ?? "";
128
141
  const normalized = entrypoint.replaceAll("\\", "/");
129
142
  if (normalized.includes("/_npx/")) {
130
- if (runtime.packageSpec !== undefined && runtime.packageSpec !== "agent-skillboard") {
131
- return `npm exec --yes --package ${shellQuote(runtime.packageSpec)} -- skillboard`;
132
- }
133
- return "npx agent-skillboard";
143
+ const packageSpec = runtime.packageSpec ?? "agent-skillboard";
144
+ return `npx --yes --package ${shellQuote(packageSpec)} skillboard`;
134
145
  }
135
146
  if (isSourceTreeEntrypoint(entrypoint)) {
136
147
  return `node ${sourceTreeEntrypoint(entrypoint, runtime.cwd ?? process.cwd())}`;
@@ -20,31 +20,38 @@ export function bridgeBlock() {
20
20
  return `${BRIDGE_START}
21
21
  # SkillBoard Control Plane
22
22
 
23
- This project uses SkillBoard as the source of truth for agent skill activation.
23
+ Your responsibility is to answer skill availability questions from SkillBoard, translate user intent into current action ids, ask for one confirmation only before policy-changing actions, apply one current action, reread the post-apply brief, and run the guard automatically before invocation.
24
24
 
25
- - Read \`skillboard.config.yaml\` before assuming an installed skill is active.
26
- - Installed \`SKILL.md\` files are not automatically callable.
27
- - When a user asks what skills are available, run \`skillboard brief --json --config skillboard.config.yaml --skills skills\` before answering. If the workflow is known, include \`--workflow <name>\`; add \`--include-actions\` only when the user wants machine-readable change suggestions.
25
+ ## Product Goal
26
+
27
+ - SkillBoard is a non-blocking AI skill routing control plane, not a pre-task settings checklist.
28
+ - Read \`docs/ai-skill-routing-goal.md\` before changing routing, brief, bridge, policy, or workflow UX.
29
+ - Preserve the loop: observe → route → work → explain briefly → ask after → remember policy.
30
+ - SkillBoard does not rewrite \`SKILL.md\` bodies to personalize behavior; record usage policy for when to use, prefer, reference, avoid, or block skills.
31
+
32
+ ## Availability
33
+
34
+ - Use SkillBoard as the source of truth; installed \`SKILL.md\` files are not automatically callable.
35
+ - Read the current brief before answering: \`skillboard brief --json --config skillboard.config.yaml --skills skills\`. If the workflow is known, include \`--workflow <name>\`; add \`--include-actions\` when the user wants you to mediate a change.
36
+ - When the user asks which skill fits a task, read \`skillboard brief --intent <request> --json --config skillboard.config.yaml --skills skills\`. Include \`--workflow <name>\` when known. Read \`assistant_guidance.route\`; use \`recommended_skill\`, \`fallback_skills\`, \`route_candidates\`, \`post_use_policy_suggestion\`, and \`guard_command\` instead of guessing from raw skill text. Inspect \`route_candidates\` when several skills match so denied candidates and selected fallbacks are clear. If \`post_use_policy_suggestion\` is present, work first with the allowed routed skill, then ask after completion whether to remember the suggested policy. If no skill matches, ask a clarifying question before choosing a skill.
28
37
  - Treat the brief sections headed "What your AI can use now", "Needs your decision", and "Blocked for safety" as the availability summary; do not infer availability from \`SKILL.md\` bodies.
29
38
  - Treat "Needs your decision" as a one-time decision queue, not a persistent blocked state. "Blocked for safety" means the skill/source/workflow is hard-blocked until policy or provenance changes.
30
39
  - Use \`skillboard can-use <skill-id> --workflow <name> --config skillboard.config.yaml --skills skills --json\` for machine-readable agent decisions.
31
- - Approval loop for action cards: read the current \`skillboard brief --json\`, pick one current action id, ask the user for confirmation, then run \`skillboard apply-action <action-id> --config skillboard.config.yaml --skills skills --yes --json\` and include \`--workflow <name>\` when a workflow is selected.
32
- - After \`skillboard apply-action <action-id> ... --yes --json\`, read the returned post-apply brief before answering the next availability question or applying another action. \`apply-action\` re-resolves current actions; do not apply cached or stale action ids, do not apply multiple actions, and do not run raw action-card shell text as the primary apply path.
33
- - Immediately before actual invocation, run \`skillboard guard use <skill-id> --workflow <name> --config skillboard.config.yaml --skills skills\`.
34
- - Run \`skillboard audit sources --config skillboard.config.yaml --skills skills\` before trusting newly imported external skill sources.
35
- - Run \`skillboard review install-unit <unit-id> --trust-level reviewed --config skillboard.config.yaml --skills skills\` after reviewing an external source and before enabling automatic invocation from it.
36
- - Manual underlying hook detail: preview guard hook installation with \`skillboard hook install --workflow <name> --config skillboard.config.yaml --skills skills --out .skillboard/hooks/<name>-guard.sh --dry-run --json\`; inspect \`planned.preview.shell\` before materializing an executable guard hook outside the action-card approval loop.
37
- - Prefer workflow-scoped skills over global skill invocation.
38
- - Only \`global-meta\` skills may be treated as globally available.
39
- - Run \`skillboard doctor --config skillboard.config.yaml --skills skills\` or \`skillboard status --config skillboard.config.yaml --skills skills --json\` to inspect control-plane health; add \`--strict\` when review-needed safe mode should fail automation.
40
- - Run \`skillboard check --config skillboard.config.yaml --skills skills\` when policy state matters.
41
- - Run \`skillboard dashboard --config skillboard.config.yaml --skills skills --out .skillboard/reports/skill-map.md\` to refresh the visible control map.
42
- - Run \`skillboard add skill <skill-id> --path <relative-skill-path> --config skillboard.config.yaml --skills skills\` to register a user-owned skill without treating the file as automatically active.
43
- - Run \`skillboard add harness <harness-name> --config skillboard.config.yaml --skills skills\` and \`skillboard add workflow <workflow-name> --harness <harness-name> --config skillboard.config.yaml --skills skills --skill <skill-id>\` to add local growth paths without editing YAML by hand.
44
- - Use \`skillboard activate <skill-id> --workflow <name> --config skillboard.config.yaml --skills skills\` and \`skillboard block <skill-id> --workflow <name> --config skillboard.config.yaml --skills skills\` for workflow-scoped enablement.
45
- - Use \`skillboard remove skill <skill-id> --config skillboard.config.yaml --skills skills --force\` only after reviewing \`skillboard impact disable <skill-id> --config skillboard.config.yaml --skills skills\`; removal updates policy references while leaving \`SKILL.md\` files on disk.
46
- - Run \`skillboard inventory refresh --dry-run --config skillboard.config.yaml\` after installing a new local agent skill pack, plugin, workflow bundle, or harness.
47
- - Run \`skillboard import --profile <id-or-path> --source-root <repo> --out .skillboard/reports/import-fragment.yaml\` after installing a new skill repository, then review the fragment before merging it into \`skillboard.config.yaml\`.
40
+
41
+ ## Intent to Action
42
+
43
+ - Translate user intent into current action ids from the current brief, not saved output.
44
+ - For action cards, read \`skillboard brief --json --config skillboard.config.yaml --skills skills --include-actions\`, pick one current action id, ask the user for one confirmation, then run \`skillboard apply-action <action-id> --config skillboard.config.yaml --skills skills --yes --json\`. Include \`--workflow <name>\` when a workflow is selected.
45
+ - After \`skillboard apply-action <action-id> ... --yes --json\`, reread the returned post-apply brief before answering the next availability question or applying another action. \`apply-action\` re-resolves current actions; do not apply cached or stale action ids, do not apply multiple actions, and do not run raw action-card shell text as the primary apply path.
46
+ - For an already-allowed skill, do not ask for another approval. Run \`skillboard guard use <skill-id> --workflow <name> --config skillboard.config.yaml --skills skills\` automatically. Say at the start: "I will use <skill-id> for this request." Say at completion: "I used <skill-id> for this request." Treat this disclosure as an audit trace, not a permission prompt. Ask the user only if the guard denies use or a policy-changing action is needed.
47
+
48
+ ## Operations
49
+
50
+ - Source trust: run \`skillboard audit sources --config skillboard.config.yaml --skills skills\` before trusting newly imported external skill sources; after review, use \`skillboard review install-unit <unit-id> --trust-level reviewed --config skillboard.config.yaml --skills skills\`. Unreviewed runtime sources are a one-time review decision, not a default block recommendation; after review, activate only the needed quarantined skills as manual-only workflow skills and use ask-after policy suggestions for future preferences.
51
+ - Health: run \`skillboard doctor --config skillboard.config.yaml --skills skills\` or \`skillboard status --config skillboard.config.yaml --skills skills --json\`; add \`--strict\` when review-needed safe mode should fail automation.
52
+ - Hook action cards: prefer \`skillboard apply-action <action-id> --yes --json\`. The underlying manual preview is \`skillboard hook install --workflow <name> --config skillboard.config.yaml --skills skills --out .skillboard/hooks/<name>-guard.sh --dry-run --json\`; inspect \`planned.preview.shell\` before materializing an executable guard hook outside the action-card control loop. Generated hooks pin the install-time SkillBoard command, config, skills root, and workflow; set those values with hook install options such as \`--skillboard-bin\`, not runtime environment overrides.
53
+ - Inventory/import growth: run \`skillboard inventory refresh --dry-run --config skillboard.config.yaml\` after installing local skill packs, plugins, workflow bundles, or harnesses; run \`skillboard import --profile <id-or-path> --source-root <repo> --out .skillboard/reports/import-fragment.yaml\` after installing a new skill repository, then review the fragment before merging it into \`skillboard.config.yaml\`.
54
+ - Prefer workflow-scoped skills over global skill invocation. Only \`global-meta\` skills may be treated as globally available.
48
55
 
49
56
  ${BRIDGE_END}`;
50
57
  }
@@ -77,11 +84,14 @@ skillboard hook install --workflow <name> --config skillboard.config.yaml --skil
77
84
  skillboard hook install --workflow <name> --config skillboard.config.yaml --skills skills --out .skillboard/hooks/<name>-guard.sh
78
85
  \`\`\`
79
86
 
80
- For hook action cards, prefer the bridge approval loop with
87
+ For hook action cards, prefer the bridge control loop with
81
88
  \`skillboard apply-action <action-id> --yes --json\`. These raw hook commands
82
89
  are the underlying manual detail: preview the JSON plan first and inspect
83
90
  \`planned.preview.shell\` before materializing an executable guard hook.
84
- The generated script delegates to \`skillboard guard use\` and can be wired into
85
- the hook mechanism of the active harness.
91
+ Generated hooks pin the install-time SkillBoard command, config, skills root,
92
+ and workflow; set those values with hook install options such as
93
+ \`--skillboard-bin\`, not runtime environment overrides. The generated script
94
+ delegates to \`skillboard guard use\` and can be wired into the hook mechanism of
95
+ the active harness.
86
96
  `;
87
97
  }