agent-skillboard 0.2.15 → 0.2.16
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 +21 -0
- package/README.md +64 -35
- package/bin/postinstall.mjs +5 -3
- package/docs/ai-skill-routing-goal.md +15 -3
- package/docs/install.md +98 -68
- package/docs/positioning.md +8 -6
- package/docs/reference.md +22 -10
- package/docs/routing.md +15 -0
- package/docs/user-flow.md +61 -32
- package/docs/value-proof.md +6 -3
- package/package.json +3 -2
- package/src/advisor/guidance.mjs +23 -3
- package/src/agent-integration-cli.mjs +184 -0
- package/src/agent-integration-content.mjs +48 -0
- package/src/agent-integration-files.mjs +202 -0
- package/src/agent-integration-home.mjs +185 -0
- package/src/agent-skill-roots.mjs +1 -0
- package/src/brief-renderer.mjs +36 -57
- package/src/cli.mjs +168 -169
- package/src/control/can-use-guard.mjs +12 -0
- package/src/doctor.mjs +4 -1
- package/src/lifecycle-cli.mjs +23 -303
- package/src/lifecycle-content.mjs +4 -2
- package/src/route-advisory.mjs +214 -0
- package/src/route-renderer.mjs +146 -0
- package/src/route-selection.mjs +220 -0
- package/src/route-tokens.mjs +68 -0
- package/src/route.mjs +48 -413
- package/src/source-profiles.mjs +151 -150
- package/src/uninstall.mjs +56 -9
- package/src/workspace.mjs +79 -79
package/docs/routing.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Capability routing helps an AI choose a skill for the user's current request
|
|
4
4
|
without making the user learn SkillBoard commands.
|
|
5
5
|
|
|
6
|
+
SkillBoard is intentionally permissive at this layer: workflow-bound skills can
|
|
7
|
+
remain broadly available, and routing only chooses the skill that should steer
|
|
8
|
+
this request. When several allowed skills match, the response includes
|
|
9
|
+
`overlap_resolution` so the AI can explain that the other skills stayed
|
|
10
|
+
available while the workflow routed to one selected skill.
|
|
11
|
+
|
|
6
12
|
For the normal AI-mediated flow, prefer `brief --intent`. It returns the
|
|
7
13
|
availability brief and the routing result together:
|
|
8
14
|
|
|
@@ -25,6 +31,8 @@ Read `assistant_guidance.route` from the JSON output. It includes:
|
|
|
25
31
|
- `recommended_skill`
|
|
26
32
|
- `fallback_skills`
|
|
27
33
|
- `route_candidates`
|
|
34
|
+
- `overlap_resolution`
|
|
35
|
+
- `policy_memory`
|
|
28
36
|
- `post_use_policy_suggestion`
|
|
29
37
|
- `guard_command`
|
|
30
38
|
- `usage_disclosure`
|
|
@@ -42,6 +50,13 @@ When several skills match, inspect `route_candidates` before acting. Each entry
|
|
|
42
50
|
shows the candidate skill, whether it was selected, whether the guard currently
|
|
43
51
|
allows it, and the guard reason when it is denied. This is the field that tells
|
|
44
52
|
an AI why a preferred skill was skipped and an allowed fallback was selected.
|
|
53
|
+
Inspect `overlap_resolution` first when it is present: `status: resolved` with
|
|
54
|
+
`mode: permissive-routing` means multiple allowed skills matched, SkillBoard
|
|
55
|
+
kept them available, and the current workflow still has one deterministic route.
|
|
56
|
+
When `policy_memory` is present, remembered or configured workflow policy
|
|
57
|
+
selected the routed skill while other allowed skills were also available. The AI
|
|
58
|
+
should mention that after completion so the user knows a stored policy
|
|
59
|
+
preference shaped the route.
|
|
45
60
|
|
|
46
61
|
When routing is safe but policy learning would reduce future ambiguity,
|
|
47
62
|
SkillBoard may return `post_use_policy_suggestion`. This includes cases where a
|
package/docs/user-flow.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# First-Time Skill Control Flow
|
|
2
2
|
|
|
3
|
-
This flow assumes a user installed SkillBoard
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
This flow assumes a user installed SkillBoard so their AI can keep installed
|
|
4
|
+
skills broadly available while resolving confusing overlap only when it matters.
|
|
5
|
+
|
|
6
|
+
When you ask your AI normal work requests such as "write tests before
|
|
7
|
+
implementation", "review this plan and point out weak assumptions", or "help me
|
|
8
|
+
refine this UX flow," the AI should work normally. SkillBoard becomes relevant
|
|
9
|
+
when skill choice is ambiguous, several skills overlap, workflow priority
|
|
10
|
+
matters, or you explicitly ask for a SkillBoard or skill decision such as "what
|
|
11
|
+
skills can you use here?" or "make this reviewed skill available for this
|
|
12
|
+
workflow." In those cases, the AI should read the current brief, show the
|
|
13
|
+
relevant choice, ask only before applying one current action id when policy
|
|
10
14
|
would change, and run the final guard automatically before invocation. For an
|
|
11
15
|
already-allowed skill, the AI should state which skill it is about to use and
|
|
12
16
|
which skill it used when reporting the result, not ask for another approval.
|
|
13
|
-
That disclosure is an audit trace, not a permission prompt.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
That disclosure is an audit trace, not a permission prompt. If you explicitly
|
|
18
|
+
ask for a specific already-allowed skill, the AI should honor that request after
|
|
19
|
+
guard use instead of rerouting away solely because another skill also matches.
|
|
20
|
+
You do not need to memorize the SkillBoard command loop. The command examples
|
|
21
|
+
below are AI/automation/operator details for the agent, scripts, or people
|
|
22
|
+
maintaining the setup.
|
|
17
23
|
|
|
18
24
|
If you ask OpenCode to use a skill you previously used in Codex, the target
|
|
19
25
|
agent should call:
|
|
@@ -73,8 +79,9 @@ returned post-apply brief before making the next availability claim.
|
|
|
73
79
|
`apply-action` re-resolves the current brief and refuses stale action ids instead
|
|
74
80
|
of replaying cached action-card shell text.
|
|
75
81
|
|
|
76
|
-
When
|
|
77
|
-
|
|
82
|
+
When a normal request leaves skill choice ambiguous, several skills overlap,
|
|
83
|
+
workflow priority matters, or the user asks for a SkillBoard or skill decision,
|
|
84
|
+
the agent can keep the same brief flow and include the user request as intent:
|
|
78
85
|
|
|
79
86
|
```bash
|
|
80
87
|
skillboard brief --intent "write tests before implementation" --workflow daily-workflow --config skillboard.config.yaml --skills skills --json
|
|
@@ -83,20 +90,29 @@ skillboard brief --intent "write tests before implementation" --workflow daily-w
|
|
|
83
90
|
The returned `assistant_guidance.route` maps the request to a declared workflow
|
|
84
91
|
capability or a workflow-bound skill metadata match, returns the recommended
|
|
85
92
|
skill and fallbacks, and includes `match_source`, `matched_terms`,
|
|
86
|
-
`recommendation_reason`, `route_candidates`, `
|
|
87
|
-
`
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
`recommendation_reason`, `route_candidates`, `overlap_resolution`,
|
|
94
|
+
`policy_memory`,
|
|
95
|
+
`post_use_policy_suggestion`, and the `skillboard guard use ...` command that
|
|
96
|
+
still needs to pass immediately before invocation. `overlap_resolution`
|
|
97
|
+
summarizes permissive routing when several allowed skills match.
|
|
98
|
+
`route_candidates` is the per-skill decision trace: it shows which matching
|
|
99
|
+
skill was selected, which candidates were denied, and the guard reason when a
|
|
100
|
+
preferred skill was skipped for an allowed fallback.
|
|
101
|
+
`policy_memory` appears when remembered or configured workflow policy selected
|
|
102
|
+
the routed skill while other allowed skills were also available; the agent
|
|
103
|
+
should mention that after completion so the user understands the prior choice
|
|
104
|
+
shaped the route.
|
|
91
105
|
`post_use_policy_suggestion` is the ask-after-use hook: if it is present, the
|
|
92
106
|
agent should use the allowed routed skill first, then ask after completion
|
|
93
107
|
whether to remember the suggested preference. Metadata matching can
|
|
94
108
|
use declared skill id, path, category, and `SKILL.md` frontmatter
|
|
95
109
|
name/description; it does not semantically rank raw skill bodies. If a
|
|
96
110
|
recommended skill is already allowed, the agent should disclose it at the start
|
|
97
|
-
and completion rather than ask for another approval. If
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
and completion rather than ask for another approval. If the user explicitly
|
|
112
|
+
requests a specific already-allowed skill, the agent should honor that request
|
|
113
|
+
after guard use instead of rerouting away solely because another skill also
|
|
114
|
+
matches. If no capability or workflow-bound skill matches, the agent should ask
|
|
115
|
+
a clarifying question instead of guessing from raw `SKILL.md` text. Operators can still call
|
|
100
116
|
`skillboard route ...` directly when they only need the recommendation payload.
|
|
101
117
|
|
|
102
118
|
Run this again after installing agent packages, plugins, workflow bundles, or
|
|
@@ -279,26 +295,39 @@ This removes SkillBoard policy references only and leaves
|
|
|
279
295
|
|
|
280
296
|
AI/automation/operator details:
|
|
281
297
|
|
|
298
|
+
Remove managed user-agent guidance first if global install/setup made agents
|
|
299
|
+
recognize SkillBoard and you want that agent-layer footprint gone:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
skillboard uninstall --agent-layer --dry-run
|
|
303
|
+
skillboard uninstall --agent-layer
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
This removes only managed `skillboard/SKILL.md` guidance files containing the
|
|
307
|
+
SkillBoard agent integration marker. It preserves other agent skills and
|
|
308
|
+
user-authored `skillboard` skills.
|
|
309
|
+
|
|
282
310
|
```bash
|
|
283
311
|
skillboard uninstall --dry-run
|
|
284
312
|
skillboard uninstall
|
|
285
313
|
```
|
|
286
314
|
|
|
287
315
|
Uninstall removes generated bridge blocks and unchanged helper files. It
|
|
288
|
-
|
|
316
|
+
also removes `skillboard.config.yaml` and the `.skillboard/` project state by
|
|
317
|
+
default while preserving local `skills/` files and user-authored non-SkillBoard
|
|
289
318
|
content in bridge files.
|
|
290
319
|
|
|
291
|
-
|
|
320
|
+
If you want to keep project SkillBoard policy and bridge guidance, opt into
|
|
321
|
+
settings preservation explicitly:
|
|
292
322
|
|
|
293
323
|
```bash
|
|
294
|
-
skillboard uninstall --
|
|
295
|
-
skillboard uninstall --
|
|
296
|
-
skillboard init
|
|
324
|
+
skillboard uninstall --keep-settings --dry-run
|
|
325
|
+
skillboard uninstall --keep-settings
|
|
297
326
|
```
|
|
298
327
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
SkillBoard config even if it contains imported skills or workflow edits,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
`--purge` is still accepted as an explicit spelling for the default clean
|
|
329
|
+
project removal. Default removal and `--purge` both discard the current
|
|
330
|
+
SkillBoard config even if it contains imported skills or workflow edits, remove
|
|
331
|
+
generated dashboard and impact reports, and remove the entire `.skillboard/`
|
|
332
|
+
project state directory, including hooks, source caches, rollout logs, variant
|
|
333
|
+
snapshots, and profiles. Local `skills/` files stay in place.
|
package/docs/value-proof.md
CHANGED
|
@@ -17,9 +17,9 @@ surfaces a user or agent would use.
|
|
|
17
17
|
The raw list answers inventory questions: which skill declarations are connected
|
|
18
18
|
to this workflow?
|
|
19
19
|
|
|
20
|
-
SkillBoard answers
|
|
21
|
-
|
|
22
|
-
what approved action changes the next state?
|
|
20
|
+
SkillBoard answers routing questions: which skills can actually run now, why
|
|
21
|
+
are others blocked, which skill should steer the user's current request when
|
|
22
|
+
several skills overlap, and what approved action changes the next state?
|
|
23
23
|
|
|
24
24
|
In the tested fixture, the raw list can make the workflow look ready because it
|
|
25
25
|
shows `matt.tdd active workflow-auto`. SkillBoard refuses that unsafe claim and
|
|
@@ -171,6 +171,9 @@ Observed route result:
|
|
|
171
171
|
- Confidence: `high`.
|
|
172
172
|
- Recommended skill: `matt.tdd`.
|
|
173
173
|
- Fallback skill: `private.tdd-work-continuity`.
|
|
174
|
+
- Overlap resolution is exposed in route payloads when several allowed skills
|
|
175
|
+
match, so agents can explain the deterministic route without hiding the other
|
|
176
|
+
available skills.
|
|
174
177
|
- Guard command: `skillboard guard use matt.tdd ...`.
|
|
175
178
|
- Guard result for `matt.tdd`: allowed.
|
|
176
179
|
- Start disclosure: `I will use matt.tdd for this request.`
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-skillboard",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.16",
|
|
4
|
+
"description": "Keep agent skills broadly available while routing overlaps consistently.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
7
7
|
"agent-skills",
|
|
8
8
|
"skills",
|
|
9
9
|
"skill-routing",
|
|
10
|
+
"overlap-routing",
|
|
10
11
|
"workflow",
|
|
11
12
|
"codex",
|
|
12
13
|
"claude-code",
|
package/src/advisor/guidance.mjs
CHANGED
|
@@ -3,7 +3,16 @@ import { command } from "./action-core.mjs";
|
|
|
3
3
|
const GUARD_WHEN = "before invoking a skill";
|
|
4
4
|
const GOAL_DOCUMENT = Object.freeze({
|
|
5
5
|
path: "docs/ai-skill-routing-goal.md",
|
|
6
|
-
purpose: "Preserve SkillBoard as a
|
|
6
|
+
purpose: "Preserve SkillBoard as a permissive AI skill routing layer: keep skills broadly available, resolve overlaps deterministically, explain briefly, ask after use when policy learning helps, and remember usage policy without rewriting skill bodies.",
|
|
7
|
+
loop: Object.freeze([
|
|
8
|
+
"observe",
|
|
9
|
+
"route",
|
|
10
|
+
"work",
|
|
11
|
+
"explain briefly",
|
|
12
|
+
"ask after",
|
|
13
|
+
"remember policy"
|
|
14
|
+
]),
|
|
15
|
+
simplification_rule: "Concepts must justify themselves by supporting SkillBoard's routing identity, overlap resolution, policy memory, or non-blocking user flow; remove, merge, or rename only the concepts that fail that test.",
|
|
7
16
|
when_to_read: Object.freeze([
|
|
8
17
|
"before changing routing",
|
|
9
18
|
"before changing brief output",
|
|
@@ -47,6 +56,7 @@ export function buildAssistantGuidance(brief, options = {}) {
|
|
|
47
56
|
function goalDocument() {
|
|
48
57
|
return {
|
|
49
58
|
...GOAL_DOCUMENT,
|
|
59
|
+
loop: [...GOAL_DOCUMENT.loop],
|
|
50
60
|
when_to_read: [...GOAL_DOCUMENT.when_to_read]
|
|
51
61
|
};
|
|
52
62
|
}
|
|
@@ -125,12 +135,14 @@ function recommendedNextStep(status, brief, choices, route = null) {
|
|
|
125
135
|
if (route !== null) {
|
|
126
136
|
return route.recommended_skill === null
|
|
127
137
|
? "Ask a clarifying question; no workflow capability matched this request."
|
|
128
|
-
: `Use ${route.recommended_skill} for this request after the guard check passes.`;
|
|
138
|
+
: `Use ${route.recommended_skill} for this request after the guard check passes${postUsePolicyStep(route)}.`;
|
|
129
139
|
}
|
|
130
140
|
return "Run the guard check before invoking any selected skill.";
|
|
131
141
|
case "needs-decision":
|
|
132
142
|
if (route?.recommended_skill !== null && route?.guard_allowed === true) {
|
|
133
|
-
return
|
|
143
|
+
return route.post_use_policy_suggestion === null
|
|
144
|
+
? `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.`
|
|
145
|
+
: `Use ${route.recommended_skill} for this request after the guard check passes; after completion, ask whether to remember the suggested policy and handle pending review decisions unless a policy-changing action is needed now.`;
|
|
134
146
|
}
|
|
135
147
|
return firstChoice === undefined
|
|
136
148
|
? "Ask the user which pending review decision to make."
|
|
@@ -159,6 +171,12 @@ function recommendedNextStep(status, brief, choices, route = null) {
|
|
|
159
171
|
}
|
|
160
172
|
}
|
|
161
173
|
|
|
174
|
+
function postUsePolicyStep(route) {
|
|
175
|
+
return route.post_use_policy_suggestion === null
|
|
176
|
+
? ""
|
|
177
|
+
: "; after completion, ask whether to remember the suggested policy";
|
|
178
|
+
}
|
|
179
|
+
|
|
162
180
|
function routeGuidance(route) {
|
|
163
181
|
return {
|
|
164
182
|
intent: route.intent,
|
|
@@ -180,6 +198,8 @@ function routeGuidance(route) {
|
|
|
180
198
|
guard_roles: candidate.guard_roles,
|
|
181
199
|
capability_roles: candidate.capability_roles
|
|
182
200
|
})),
|
|
201
|
+
overlap_resolution: route.overlap_resolution ?? null,
|
|
202
|
+
policy_memory: route.policy_memory ?? null,
|
|
183
203
|
usage_disclosure: route.usage_disclosure ?? null,
|
|
184
204
|
post_use_policy_suggestion: route.post_use_policy_suggestion ?? null,
|
|
185
205
|
guard_command: route.guard_command,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { installAgentIntegration, uninstallAgentIntegration } from "./agent-integration-files.mjs";
|
|
4
|
+
import { resolveSetupHome, setupOwnership } from "./agent-integration-home.mjs";
|
|
5
|
+
import { setupAgentSkillTargets, supportedAgentNames } from "./agent-skill-roots.mjs";
|
|
6
|
+
|
|
7
|
+
export async function runSetupCommand(options, stdout, runtime = defaultRuntime()) {
|
|
8
|
+
if (options.get("dir") !== undefined) {
|
|
9
|
+
throw new Error("skillboard setup is agent-layer setup and does not accept --dir");
|
|
10
|
+
}
|
|
11
|
+
const env = runtime.env ?? process.env;
|
|
12
|
+
const home = await resolveSetupHome(env, runtime);
|
|
13
|
+
const targets = await agentSetupTargets(options, runtime, home);
|
|
14
|
+
if (targets.length === 0) {
|
|
15
|
+
stdout.write("No supported agent user skill roots were detected.\n");
|
|
16
|
+
stdout.write(`Create a supported agent home or pass --agent ${supportedAgentNames().join(",")} to choose targets.\n`);
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
if (options.get("yes") !== "true") {
|
|
20
|
+
writeSetupConfirmation(stdout, targets, commandPrefix(runtime));
|
|
21
|
+
if (!canPrompt(runtime)) {
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
const confirmed = await promptForSetup(runtime);
|
|
25
|
+
if (!confirmed) {
|
|
26
|
+
stdout.write("Skipped SkillBoard setup.\n");
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const result = await installAgentIntegration(targets, setupOwnership(env, runtime, home));
|
|
31
|
+
stdout.write("SkillBoard agent integration installed.\n");
|
|
32
|
+
writeList(stdout, "Created", result.created);
|
|
33
|
+
writeList(stdout, "Updated", result.updated);
|
|
34
|
+
writeList(stdout, "Unchanged", result.unchanged);
|
|
35
|
+
writeList(stdout, "Preserved", result.preserved);
|
|
36
|
+
stdout.write("Next:\n");
|
|
37
|
+
stdout.write("- Restart or refresh agents that cache user skills.\n");
|
|
38
|
+
stdout.write("- No project was initialized; run skillboard init only inside a workspace that needs local SkillBoard policy.\n");
|
|
39
|
+
stdout.write('- Ask the agent in a workspace: "Review this plan and point out weak assumptions."\n');
|
|
40
|
+
stdout.write("- SkillBoard will step in when skills overlap, routing is ambiguous, or you ask for a skill decision.\n");
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function runAgentLayerUninstallCommand(options, stdout, runtime = defaultRuntime()) {
|
|
45
|
+
if (options.get("dir") !== undefined) {
|
|
46
|
+
throw new Error("skillboard uninstall --agent-layer is agent-layer cleanup and does not accept --dir");
|
|
47
|
+
}
|
|
48
|
+
for (const option of ["remove-config", "reset-config", "purge", "keep-settings", "remove-reports", "remove-hooks"]) {
|
|
49
|
+
if (options.get(option) === "true") {
|
|
50
|
+
throw new Error(`--${option} cannot be combined with --agent-layer`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const env = runtime.env ?? process.env;
|
|
54
|
+
const home = await resolveSetupHome(env, runtime);
|
|
55
|
+
const targets = await agentSetupTargets(options, runtime, home);
|
|
56
|
+
if (targets.length === 0) {
|
|
57
|
+
stdout.write("No supported agent user skill roots were detected.\n");
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
const dryRun = options.get("dry-run") === "true";
|
|
61
|
+
const result = await uninstallAgentIntegration(targets, dryRun);
|
|
62
|
+
stdout.write(`${dryRun ? "Dry run: " : ""}Uninstalled SkillBoard agent integration.\n`);
|
|
63
|
+
writeList(stdout, "Removed", result.removed);
|
|
64
|
+
writeList(stdout, "Updated", result.updated);
|
|
65
|
+
writeList(stdout, "Preserved", result.preserved);
|
|
66
|
+
writeList(stdout, "Absent", result.absent);
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function writeList(stdout, label, values) {
|
|
71
|
+
if (values.length > 0) {
|
|
72
|
+
stdout.write(`${label}: ${formatList(values)}\n`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function writeSetupConfirmation(stdout, targets, command) {
|
|
77
|
+
stdout.write("SkillBoard setup installs agent-layer integration, not project files.\n");
|
|
78
|
+
stdout.write("It writes a SkillBoard guidance skill into detected user agent skill roots so agents can resolve skill priority when choices overlap.\n");
|
|
79
|
+
stdout.write("It does not create skillboard.config.yaml or .skillboard/; run skillboard init later only for a project that needs local policy.\n");
|
|
80
|
+
stdout.write("Targets:\n");
|
|
81
|
+
for (const target of targets) {
|
|
82
|
+
stdout.write(`- ${target.agent}: ${target.skillPath}\n`);
|
|
83
|
+
}
|
|
84
|
+
stdout.write("Run with --yes to install agent-layer integration:\n");
|
|
85
|
+
stdout.write(`- ${command} setup --agent ${targets.map((target) => target.agent).join(",")} --yes\n`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function canPrompt(runtime) {
|
|
89
|
+
return runtime.stdin?.isTTY === true && runtime.stdout?.isTTY === true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function promptForSetup(runtime) {
|
|
93
|
+
const rl = createInterface({
|
|
94
|
+
input: runtime.stdin,
|
|
95
|
+
output: runtime.stdout
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
const answer = await question(rl, "Install SkillBoard agent integration now? [y/N] ");
|
|
99
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
100
|
+
} finally {
|
|
101
|
+
rl.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function question(rl, prompt) {
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
rl.question(prompt, resolve);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function formatList(values) {
|
|
112
|
+
return values.length === 0 ? "none" : values.map((value) => `\`${value}\``).join(", ");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function readCsv(value) {
|
|
116
|
+
if (value === undefined || value.trim() === "") {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function defaultRuntime() {
|
|
123
|
+
return {
|
|
124
|
+
cwd: process.cwd(),
|
|
125
|
+
entrypointPath: process.argv[1],
|
|
126
|
+
packageSpec: process.env.npm_config_package
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function agentSetupTargets(options, runtime, setupHome) {
|
|
131
|
+
const env = runtime.env ?? process.env;
|
|
132
|
+
const home = setupHome ?? await resolveSetupHome(env, runtime);
|
|
133
|
+
const requested = readCsv(options.get("agent"));
|
|
134
|
+
const supported = new Set(supportedAgentNames());
|
|
135
|
+
const names = requested.length === 0 ? supportedAgentNames() : requested;
|
|
136
|
+
const targets = [];
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
if (!supported.has(name)) {
|
|
139
|
+
throw new Error(`Unsupported setup agent: ${name}`);
|
|
140
|
+
}
|
|
141
|
+
targets.push(...await setupAgentSkillTargets(name, home, env, { includeFallback: requested.length > 0 }));
|
|
142
|
+
}
|
|
143
|
+
return targets;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function commandPrefix(runtime) {
|
|
147
|
+
const entrypoint = runtime.entrypointPath ?? "";
|
|
148
|
+
const normalized = entrypoint.replace(/\\/g, "/");
|
|
149
|
+
if (normalized.includes("/_npx/")) {
|
|
150
|
+
const packageSpec = runtime.packageSpec ?? "agent-skillboard";
|
|
151
|
+
return `npx --yes --package ${shellQuote(packageSpec)} skillboard`;
|
|
152
|
+
}
|
|
153
|
+
if (isSourceTreeEntrypoint(entrypoint)) {
|
|
154
|
+
return `node ${sourceTreeEntrypoint(entrypoint, runtime.cwd ?? process.cwd())}`;
|
|
155
|
+
}
|
|
156
|
+
return "skillboard";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isSourceTreeEntrypoint(entrypoint) {
|
|
160
|
+
if (entrypoint === "") {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
const normalized = entrypoint.replace(/\\/g, "/");
|
|
164
|
+
return (normalized === "bin/skillboard.mjs" || normalized.endsWith("/bin/skillboard.mjs"))
|
|
165
|
+
&& !normalized.includes("/node_modules/")
|
|
166
|
+
&& !normalized.includes("/_npx/")
|
|
167
|
+
&& !normalized.includes("/.npm/");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function sourceTreeEntrypoint(entrypoint, cwd) {
|
|
171
|
+
const absoluteEntrypoint = isAbsolute(entrypoint) ? entrypoint : resolve(cwd, entrypoint);
|
|
172
|
+
const relativeEntrypoint = relative(cwd, absoluteEntrypoint).replace(/\\/g, "/");
|
|
173
|
+
if (!relativeEntrypoint.startsWith("../") && relativeEntrypoint !== ".." && !isAbsolute(relativeEntrypoint)) {
|
|
174
|
+
return shellQuote(relativeEntrypoint);
|
|
175
|
+
}
|
|
176
|
+
return shellQuote(absoluteEntrypoint);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function shellQuote(value) {
|
|
180
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) {
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
184
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const AGENT_INTEGRATION_START = "<!-- BEGIN SKILLBOARD AGENT INTEGRATION -->";
|
|
2
|
+
export const AGENT_INTEGRATION_END = "<!-- END SKILLBOARD AGENT INTEGRATION -->";
|
|
3
|
+
|
|
4
|
+
export function agentIntegrationSkill() {
|
|
5
|
+
return `---
|
|
6
|
+
name: skillboard
|
|
7
|
+
description: Use SkillBoard when several installed skills could apply, a skill choice is ambiguous, workflow priority matters, or the user explicitly asks which skill should be used, imported, preferred, avoided, reviewed, or prioritized.
|
|
8
|
+
---
|
|
9
|
+
${AGENT_INTEGRATION_START}
|
|
10
|
+
# SkillBoard Agent Integration
|
|
11
|
+
|
|
12
|
+
Use this skill to let SkillBoard guide skill selection above individual projects.
|
|
13
|
+
|
|
14
|
+
## Layering
|
|
15
|
+
|
|
16
|
+
- SkillBoard is the user-level control plane for skill priority, overlap resolution, and workflow-aware routing.
|
|
17
|
+
- Project management belongs to the agent or workspace layer. Do not initialize, attach, rewrite, or manage a project just because SkillBoard is installed.
|
|
18
|
+
- Package install and \`skillboard setup\` install user-agent guidance only.
|
|
19
|
+
- \`skillboard init\` is a separate project command. Use it only when the user wants project-local policy files such as \`skillboard.config.yaml\`, \`.skillboard/\`, \`AGENTS.md\`, or \`CLAUDE.md\`.
|
|
20
|
+
|
|
21
|
+
## Default Behavior
|
|
22
|
+
|
|
23
|
+
- Installed user skills are usable by default unless the runtime, user, or local instructions disable them.
|
|
24
|
+
- For ordinary user requests, work normally; invoke SkillBoard only when skill choice is ambiguous, skills overlap, workflow priority matters, or the user asks for a SkillBoard or skill decision.
|
|
25
|
+
- If the user explicitly asks for a specific installed skill, honor that request when the guard allows it instead of rerouting away solely because other skills also match.
|
|
26
|
+
- Do not ask for permission merely because you selected a skill.
|
|
27
|
+
- When you use a skill, disclose it briefly at the start and completion. If SkillBoard says remembered or configured policy selected this skill while other allowed skills were available, mention that at completion.
|
|
28
|
+
|
|
29
|
+
## Cross-Agent Skill Reuse
|
|
30
|
+
|
|
31
|
+
- When the user wants to use a skill from another agent, run \`skillboard import-skill --from <source-agent> --to <this-agent> --skill <skill> --json\`.
|
|
32
|
+
- If SkillBoard reports the skill is compatible, install it with \`--yes\` and use the copied target-agent skill.
|
|
33
|
+
- If SkillBoard reports \`needs-adaptation\`, explain the compatibility reasons and ask before changing the skill body for this agent.
|
|
34
|
+
- If the user approves, inspect the source skill, write an adapted \`SKILL.md\` for this agent, then install it with \`skillboard import-skill --from <source-agent> --to <this-agent> --skill <skill> --adapted-file <adapted-skill.md> --yes --json\`.
|
|
35
|
+
- This is still user-agent setup. Do not create or mutate project policy files for cross-agent skill reuse.
|
|
36
|
+
|
|
37
|
+
## Ambiguity Resolution
|
|
38
|
+
|
|
39
|
+
1. Start from the user's task, not from a pre-task inventory prompt.
|
|
40
|
+
2. Identify candidate skills only when multiple installed skills plausibly match, a manual skill-control request is present, or workflow priority could change the route.
|
|
41
|
+
3. Prefer the skill whose explicit request, description, workflow guidance, and local instructions most directly match the user's task.
|
|
42
|
+
4. If a project or agent has explicit SkillBoard policy, use \`skillboard brief --intent <request> --json\` or \`skillboard route <intent> --workflow <name> --json\` to break ties.
|
|
43
|
+
5. If the best choice is still ambiguous or the choice would change persistent policy, ask the user which priority to remember.
|
|
44
|
+
6. Continue with the selected skill; do not stop only because other candidate skills exist.
|
|
45
|
+
|
|
46
|
+
${AGENT_INTEGRATION_END}
|
|
47
|
+
`;
|
|
48
|
+
}
|