agent-skillboard 0.2.7 → 0.2.8
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 +20 -0
- package/README.md +46 -24
- package/bin/postinstall.mjs +61 -0
- package/docs/install.md +60 -25
- package/docs/reference.md +43 -0
- package/docs/user-flow.md +11 -0
- package/package.json +2 -1
- package/src/advisor/actions.mjs +1 -0
- package/src/agent-inventory-platforms.mjs +7 -14
- package/src/agent-inventory.mjs +4 -1
- package/src/agent-skill-import.mjs +291 -0
- package/src/agent-skill-roots.mjs +114 -0
- package/src/cli.mjs +134 -19
- package/src/control/skill-crud.mjs +1 -0
- package/src/doctor.mjs +1 -1
- package/src/lifecycle-cli.mjs +159 -5
- package/src/lifecycle-content.mjs +1 -1
- package/src/uninstall.mjs +1 -0
package/src/lifecycle-cli.mjs
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
4
|
+
import { setupAgentSkillTargets, supportedAgentNames } from "./agent-skill-roots.mjs";
|
|
2
5
|
import { initProject } from "./init.mjs";
|
|
3
6
|
import { uninstallProject } from "./uninstall.mjs";
|
|
4
7
|
|
|
8
|
+
const AGENT_INTEGRATION_START = "<!-- BEGIN SKILLBOARD AGENT INTEGRATION -->";
|
|
9
|
+
const AGENT_INTEGRATION_END = "<!-- END SKILLBOARD AGENT INTEGRATION -->";
|
|
10
|
+
|
|
5
11
|
export async function runInitCommand(options, stdout, runtime = defaultRuntime()) {
|
|
6
12
|
const root = resolve(options.get("dir") ?? ".");
|
|
7
13
|
const result = await initProject({
|
|
@@ -36,6 +42,39 @@ export async function runInitCommand(options, stdout, runtime = defaultRuntime()
|
|
|
36
42
|
return 0;
|
|
37
43
|
}
|
|
38
44
|
|
|
45
|
+
export async function runSetupCommand(options, stdout, runtime = defaultRuntime()) {
|
|
46
|
+
if (options.get("dir") !== undefined) {
|
|
47
|
+
throw new Error("skillboard setup is agent-layer setup and does not accept --dir");
|
|
48
|
+
}
|
|
49
|
+
const targets = await agentSetupTargets(options, runtime);
|
|
50
|
+
if (targets.length === 0) {
|
|
51
|
+
stdout.write("No supported agent user skill roots were detected.\n");
|
|
52
|
+
stdout.write(`Create a supported agent home or pass --agent ${supportedAgentNames().join(",")} to choose targets.\n`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
if (options.get("yes") !== "true") {
|
|
56
|
+
writeSetupConfirmation(stdout, targets, commandPrefix(runtime));
|
|
57
|
+
if (!canPrompt(runtime)) {
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
const confirmed = await promptForSetup(runtime);
|
|
61
|
+
if (!confirmed) {
|
|
62
|
+
stdout.write("Skipped SkillBoard setup.\n");
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const result = await installAgentIntegration(targets);
|
|
67
|
+
stdout.write("SkillBoard agent integration installed.\n");
|
|
68
|
+
writeList(stdout, "Created", result.created);
|
|
69
|
+
writeList(stdout, "Updated", result.updated);
|
|
70
|
+
writeList(stdout, "Unchanged", result.unchanged);
|
|
71
|
+
writeList(stdout, "Preserved", result.preserved);
|
|
72
|
+
stdout.write("Next:\n");
|
|
73
|
+
stdout.write("- Restart or refresh agents that cache user skills.\n");
|
|
74
|
+
stdout.write('- Ask the agent in a workspace: "Which skill should you use for this?"\n');
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
39
78
|
export async function runUninstallCommand(options, stdout) {
|
|
40
79
|
const removeConfig = options.get("remove-config") === "true";
|
|
41
80
|
const resetConfig = options.get("reset-config") === "true";
|
|
@@ -85,15 +124,15 @@ function writeCountedList(stdout, label, values) {
|
|
|
85
124
|
}
|
|
86
125
|
|
|
87
126
|
function writeSafetyDefault(stdout, safety) {
|
|
88
|
-
stdout.write("
|
|
89
|
-
stdout.write("- Installed
|
|
127
|
+
stdout.write("Skill selection default:\n");
|
|
128
|
+
stdout.write("- Installed user skills are usable unless runtime, user, or local instructions disable them.\n");
|
|
90
129
|
if (safety.automatic === 0) {
|
|
91
130
|
stdout.write("- No automatic model invocation was enabled.\n");
|
|
92
131
|
} else {
|
|
93
132
|
stdout.write(`- ${safety.automatic} automatic skills enabled by existing policy.\n`);
|
|
94
133
|
}
|
|
95
|
-
stdout.write("- Imported local skills are
|
|
96
|
-
stdout.write("- Runtime/plugin/system skills require source review before
|
|
134
|
+
stdout.write("- Imported local skills are available on request in generated local policy.\n");
|
|
135
|
+
stdout.write("- Runtime/plugin/system skills require source review before automatic invocation.\n");
|
|
97
136
|
stdout.write(`- ${safety.automatic} automatic skills enabled\n`);
|
|
98
137
|
stdout.write(`- ${safety.manualOnly} manual-only skills available\n`);
|
|
99
138
|
stdout.write(`- ${safety.routerOnly} router-only skills available\n`);
|
|
@@ -120,6 +159,34 @@ function writeNextCommands(stdout, next) {
|
|
|
120
159
|
stdout.write(`- ${next.command} brief --workflow ${shellQuote(workflow)}${dir} --verbose\n`);
|
|
121
160
|
}
|
|
122
161
|
|
|
162
|
+
function writeSetupConfirmation(stdout, targets, command) {
|
|
163
|
+
stdout.write("SkillBoard setup installs agent-layer integration, not project files.\n");
|
|
164
|
+
stdout.write("It writes a SkillBoard guidance skill into detected user agent skill roots so agents can resolve skill priority when choices overlap.\n");
|
|
165
|
+
stdout.write("Targets:\n");
|
|
166
|
+
for (const target of targets) {
|
|
167
|
+
stdout.write(`- ${target.agent}: ${target.skillPath}\n`);
|
|
168
|
+
}
|
|
169
|
+
stdout.write("Run with --yes to install agent-layer integration:\n");
|
|
170
|
+
stdout.write(`- ${command} setup --agent ${targets.map((target) => target.agent).join(",")} --yes\n`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function canPrompt(runtime) {
|
|
174
|
+
return runtime.stdin?.isTTY === true && runtime.stdout?.isTTY === true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function promptForSetup(runtime) {
|
|
178
|
+
const rl = createInterface({
|
|
179
|
+
input: runtime.stdin,
|
|
180
|
+
output: runtime.stdout
|
|
181
|
+
});
|
|
182
|
+
try {
|
|
183
|
+
const answer = await rl.question("Install SkillBoard agent integration now? [y/N] ");
|
|
184
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
185
|
+
} finally {
|
|
186
|
+
rl.close();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
123
190
|
function formatList(values) {
|
|
124
191
|
return values.length === 0 ? "none" : values.map((value) => `\`${value}\``).join(", ");
|
|
125
192
|
}
|
|
@@ -143,6 +210,93 @@ function defaultRuntime() {
|
|
|
143
210
|
};
|
|
144
211
|
}
|
|
145
212
|
|
|
213
|
+
async function agentSetupTargets(options, runtime) {
|
|
214
|
+
const env = runtime.env ?? process.env;
|
|
215
|
+
const home = env.HOME ?? env.USERPROFILE;
|
|
216
|
+
const requested = readCsv(options.get("agent"));
|
|
217
|
+
const supported = new Set(supportedAgentNames());
|
|
218
|
+
const names = requested.length === 0 ? supportedAgentNames() : requested;
|
|
219
|
+
const targets = [];
|
|
220
|
+
for (const name of names) {
|
|
221
|
+
if (!supported.has(name)) {
|
|
222
|
+
throw new Error(`Unsupported setup agent: ${name}`);
|
|
223
|
+
}
|
|
224
|
+
targets.push(...await setupAgentSkillTargets(name, home, env, { includeFallback: requested.length > 0 }));
|
|
225
|
+
}
|
|
226
|
+
return targets;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function installAgentIntegration(targets) {
|
|
230
|
+
const created = [];
|
|
231
|
+
const updated = [];
|
|
232
|
+
const unchanged = [];
|
|
233
|
+
const preserved = [];
|
|
234
|
+
const content = agentIntegrationSkill();
|
|
235
|
+
for (const target of targets) {
|
|
236
|
+
const existing = await readFile(target.skillPath, "utf8").catch((error) => {
|
|
237
|
+
if (error?.code === "ENOENT") {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
throw error;
|
|
241
|
+
});
|
|
242
|
+
if (existing === content) {
|
|
243
|
+
unchanged.push(`${target.agent}:${target.skillPath}`);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (existing !== null && !existing.includes(AGENT_INTEGRATION_START)) {
|
|
247
|
+
preserved.push(`${target.agent}:${target.skillPath}`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
await mkdir(dirname(target.skillPath), { recursive: true });
|
|
251
|
+
await writeFile(target.skillPath, content, "utf8");
|
|
252
|
+
(existing === null ? created : updated).push(`${target.agent}:${target.skillPath}`);
|
|
253
|
+
}
|
|
254
|
+
return { created, updated, unchanged, preserved };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function agentIntegrationSkill() {
|
|
258
|
+
return `---
|
|
259
|
+
name: skillboard
|
|
260
|
+
description: Use SkillBoard when several installed skills could apply, a skill choice is ambiguous, or the user asks which skill should be used, preferred, avoided, reviewed, or prioritized for the current workflow.
|
|
261
|
+
---
|
|
262
|
+
${AGENT_INTEGRATION_START}
|
|
263
|
+
# SkillBoard Agent Integration
|
|
264
|
+
|
|
265
|
+
Use this skill to let SkillBoard guide skill selection above individual projects.
|
|
266
|
+
|
|
267
|
+
## Layering
|
|
268
|
+
|
|
269
|
+
- SkillBoard is the user-level control plane for skill priority, overlap resolution, and workflow-aware routing.
|
|
270
|
+
- Project management belongs to the agent or workspace layer. Do not initialize, attach, rewrite, or manage a project just because SkillBoard is installed.
|
|
271
|
+
- Package install and \`skillboard setup\` install user-agent guidance only.
|
|
272
|
+
|
|
273
|
+
## Default Behavior
|
|
274
|
+
|
|
275
|
+
- Installed user skills are usable by default unless the runtime, user, or local instructions disable them.
|
|
276
|
+
- Do not ask for permission merely because you selected a skill.
|
|
277
|
+
- When you use a skill, disclose it briefly at the start and completion.
|
|
278
|
+
- Use SkillBoard when more than one skill matches, when skills overlap, or when workflow priority should choose between plausible skills.
|
|
279
|
+
|
|
280
|
+
## Cross-Agent Skill Reuse
|
|
281
|
+
|
|
282
|
+
- When the user wants to use a skill from another agent, run \`skillboard import-skill --from <source-agent> --to <this-agent> --skill <skill> --json\`.
|
|
283
|
+
- If SkillBoard reports the skill is compatible, install it with \`--yes\` and use the copied target-agent skill.
|
|
284
|
+
- If SkillBoard reports \`needs-adaptation\`, explain the compatibility reasons and ask before changing the skill body for this agent.
|
|
285
|
+
- 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\`.
|
|
286
|
+
- This is still user-agent setup. Do not create or mutate project policy files for cross-agent skill reuse.
|
|
287
|
+
|
|
288
|
+
## Ambiguity Resolution
|
|
289
|
+
|
|
290
|
+
1. Identify the candidate skills that match the request.
|
|
291
|
+
2. Prefer the skill whose description, workflow guidance, and local instructions most directly match the user's task.
|
|
292
|
+
3. 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.
|
|
293
|
+
4. If the best choice is still ambiguous or the choice would change persistent policy, ask the user which priority to remember.
|
|
294
|
+
5. Continue with the selected skill; do not stop only because other candidate skills exist.
|
|
295
|
+
|
|
296
|
+
${AGENT_INTEGRATION_END}
|
|
297
|
+
`;
|
|
298
|
+
}
|
|
299
|
+
|
|
146
300
|
function commandPrefix(runtime) {
|
|
147
301
|
const entrypoint = runtime.entrypointPath ?? "";
|
|
148
302
|
const normalized = entrypoint.replaceAll("\\", "/");
|
|
@@ -31,7 +31,7 @@ Your responsibility is to answer skill availability questions from SkillBoard, t
|
|
|
31
31
|
|
|
32
32
|
## Availability
|
|
33
33
|
|
|
34
|
-
- Use SkillBoard as the source of truth; installed \`SKILL.md\` files are not
|
|
34
|
+
- Use SkillBoard as the source of truth for project-local policy and workflow priority; installed \`SKILL.md\` files are candidates, not enough to resolve overlap by themselves.
|
|
35
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
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.
|
|
37
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.
|
package/src/uninstall.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// allow: SIZE_OK - uninstall lifecycle split is deferred from the 0.2.7 release gate.
|
|
1
2
|
import { access, lstat, readFile, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { BRIDGE_END, BRIDGE_START, defaultConfig, hookReadme, profileReadme } from "./lifecycle-content.mjs";
|