agent-skillboard 0.2.16 → 0.2.18

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/src/cli.mjs CHANGED
@@ -142,8 +142,8 @@ async function run(argv, stdout, stderr, stdin) {
142
142
  return await sources(argv.slice(1), options, stdout);
143
143
  case "import":
144
144
  return await importProfile(options, stdout);
145
- case "scan":
146
- return await scan(options, stdout);
145
+ case "scan":
146
+ return await scan(options, stdout);
147
147
  case "check":
148
148
  return await check(options, stdout, stderr);
149
149
  case "doctor":
@@ -192,10 +192,10 @@ async function run(argv, stdout, stderr, stdin) {
192
192
  return await remove(argv.slice(1), options, stdout);
193
193
  case "dashboard":
194
194
  return await dashboard(options, stdout);
195
- case "reconcile":
196
- return await reconcile(options, stdout);
197
- case "impact":
198
- return await impact(argv.slice(1), options, stdout);
195
+ case "reconcile":
196
+ return await reconcile(options, stdout);
197
+ case "impact":
198
+ return await impact(argv.slice(1), options, stdout);
199
199
  case "help":
200
200
  case "--help":
201
201
  case "-h":
@@ -224,30 +224,30 @@ function selectedHelpText(command, args, options) {
224
224
  }
225
225
  return null;
226
226
  }
227
-
227
+
228
228
  async function importProfile(options, stdout) {
229
- const profileRef = options.get("profile");
230
- const sourceRoot = options.get("source-root");
231
- if (profileRef === undefined || sourceRoot === undefined) {
232
- throw new Error("Usage: skillboard import --profile <id-or-path> --source-root <dir>");
233
- }
234
- const profile = await loadSourceProfile(profileRef, { profileDirs: readCsv(options.get("profile-dirs")) });
235
- const imported = await importSource({ profile, sourceRoot });
236
- if (options.get("merge") === "true") {
237
- return await mergeImport(options, imported, stdout);
238
- }
239
- const fragment = renderImportFragment(imported);
240
- const out = options.get("out");
241
- if (out === undefined) {
242
- stdout.write(fragment);
243
- return 0;
244
- }
245
- await mkdir(dirname(out), { recursive: true });
246
- await writeFile(out, fragment, "utf8");
247
- stdout.write(`Import fragment written: ${out}\n`);
248
- return 0;
249
- }
250
-
229
+ const profileRef = options.get("profile");
230
+ const sourceRoot = options.get("source-root");
231
+ if (profileRef === undefined || sourceRoot === undefined) {
232
+ throw new Error("Usage: skillboard import --profile <id-or-path> --source-root <dir>");
233
+ }
234
+ const profile = await loadSourceProfile(profileRef, { profileDirs: readCsv(options.get("profile-dirs")) });
235
+ const imported = await importSource({ profile, sourceRoot });
236
+ if (options.get("merge") === "true") {
237
+ return await mergeImport(options, imported, stdout);
238
+ }
239
+ const fragment = renderImportFragment(imported);
240
+ const out = options.get("out");
241
+ if (out === undefined) {
242
+ stdout.write(fragment);
243
+ return 0;
244
+ }
245
+ await mkdir(dirname(out), { recursive: true });
246
+ await writeFile(out, fragment, "utf8");
247
+ stdout.write(`Import fragment written: ${out}\n`);
248
+ return 0;
249
+ }
250
+
251
251
  async function mergeImport(options, imported, stdout) {
252
252
  const path = configPath(options);
253
253
  const originalText = await readFile(path, "utf8");
@@ -337,21 +337,21 @@ async function sources(argv, options, stdout) {
337
337
  writeOutput(stdout, result, options, () => renderSourceRefresh(result));
338
338
  return 0;
339
339
  }
340
-
340
+
341
341
  async function scan(options, stdout) {
342
- const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
343
- stdout.write(`${JSON.stringify(workspace, null, 2)}\n`);
344
- return 0;
345
- }
346
-
342
+ const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
343
+ stdout.write(`${JSON.stringify(workspace, null, 2)}\n`);
344
+ return 0;
345
+ }
346
+
347
347
  async function check(options, stdout, stderr) {
348
348
  const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
349
349
  const result = checkPolicy(workspace);
350
- const output = [...result.errors, ...result.warnings].join("\n");
351
- if (output.length > 0) {
352
- (result.ok ? stdout : stderr).write(`${output}\n`);
353
- }
354
- stdout.write(result.ok ? "Policy check passed\n" : "Policy check failed\n");
350
+ const output = [...result.errors, ...result.warnings].join("\n");
351
+ if (output.length > 0) {
352
+ (result.ok ? stdout : stderr).write(`${output}\n`);
353
+ }
354
+ stdout.write(result.ok ? "Policy check passed\n" : "Policy check failed\n");
355
355
  return result.ok ? 0 : 1;
356
356
  }
357
357
 
@@ -983,37 +983,37 @@ async function remove(argv, options, stdout) {
983
983
  }
984
984
 
985
985
  async function dashboard(options, stdout) {
986
- const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
987
- const markdown = renderDashboard(workspace);
988
- const out = options.get("out");
989
- if (out === undefined) {
990
- stdout.write(markdown);
991
- return 0;
992
- }
993
- await mkdir(dirname(out), { recursive: true });
994
- await writeFile(out, markdown, "utf8");
995
- stdout.write(`Dashboard written: ${out}\n`);
996
- return 0;
997
- }
998
-
999
- async function reconcile(options, stdout) {
1000
- const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
1001
- const plan = reconcileWorkspace(workspace, { actualHarnesses: readCsv(options.get("actual-harnesses")) });
1002
- const markdown = renderReconcilePlan(plan);
1003
- const out = options.get("out");
1004
- if (out === undefined) {
1005
- stdout.write(markdown);
1006
- return 0;
1007
- }
1008
- await mkdir(dirname(out), { recursive: true });
1009
- await writeFile(out, markdown, "utf8");
1010
- stdout.write(`Reconcile plan written: ${out}\n`);
1011
- return 0;
1012
- }
1013
-
1014
- async function impact(argv, options, stdout) {
1015
- if (argv[0] !== "disable" || argv[1] === undefined) {
1016
- throw new Error("Usage: skillboard impact disable <skill-id>");
986
+ const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
987
+ const markdown = renderDashboard(workspace);
988
+ const out = options.get("out");
989
+ if (out === undefined) {
990
+ stdout.write(markdown);
991
+ return 0;
992
+ }
993
+ await mkdir(dirname(out), { recursive: true });
994
+ await writeFile(out, markdown, "utf8");
995
+ stdout.write(`Dashboard written: ${out}\n`);
996
+ return 0;
997
+ }
998
+
999
+ async function reconcile(options, stdout) {
1000
+ const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
1001
+ const plan = reconcileWorkspace(workspace, { actualHarnesses: readCsv(options.get("actual-harnesses")) });
1002
+ const markdown = renderReconcilePlan(plan);
1003
+ const out = options.get("out");
1004
+ if (out === undefined) {
1005
+ stdout.write(markdown);
1006
+ return 0;
1007
+ }
1008
+ await mkdir(dirname(out), { recursive: true });
1009
+ await writeFile(out, markdown, "utf8");
1010
+ stdout.write(`Reconcile plan written: ${out}\n`);
1011
+ return 0;
1012
+ }
1013
+
1014
+ async function impact(argv, options, stdout) {
1015
+ if (argv[0] !== "disable" || argv[1] === undefined) {
1016
+ throw new Error("Usage: skillboard impact disable <skill-id>");
1017
1017
  }
1018
1018
  const workspace = await loadWorkspace({ configPath: configPath(options), skillsRoot: skillsRoot(options) });
1019
1019
  const result = impactDisable(workspace, argv[1]);
@@ -1023,22 +1023,22 @@ async function impact(argv, options, stdout) {
1023
1023
  }
1024
1024
  const markdown = renderImpact(result);
1025
1025
  const out = options.get("out");
1026
- if (out === undefined) {
1027
- stdout.write(markdown);
1028
- return 0;
1029
- }
1030
- await mkdir(dirname(out), { recursive: true });
1031
- await writeFile(out, markdown, "utf8");
1032
- stdout.write(`Impact report written: ${out}\n`);
1033
- return 0;
1034
- }
1035
-
1026
+ if (out === undefined) {
1027
+ stdout.write(markdown);
1028
+ return 0;
1029
+ }
1030
+ await mkdir(dirname(out), { recursive: true });
1031
+ await writeFile(out, markdown, "utf8");
1032
+ stdout.write(`Impact report written: ${out}\n`);
1033
+ return 0;
1034
+ }
1035
+
1036
1036
  function renderImpact(result) {
1037
- return [
1038
- `# Disable Impact: ${result.skillId}`,
1039
- "",
1040
- `- Found: \`${result.exists}\``,
1041
- `- Risk: ${result.risk}`,
1037
+ return [
1038
+ `# Disable Impact: ${result.skillId}`,
1039
+ "",
1040
+ `- Found: \`${result.exists}\``,
1041
+ `- Risk: ${result.risk}`,
1042
1042
  `- Affected workflows: ${formatList(result.affectedWorkflows)}`,
1043
1043
  `- Affected required outputs: ${formatList(result.affectedOutputs)}`,
1044
1044
  `- Alternatives: ${formatList(result.alternatives)}`,
@@ -1075,24 +1075,24 @@ function commandRoot(options) {
1075
1075
  }
1076
1076
 
1077
1077
  function parseOptions(args) {
1078
- const options = new Map();
1079
- for (let index = 0; index < args.length; index += 1) {
1080
- const arg = args[index];
1081
- if (!arg.startsWith("--")) {
1082
- continue;
1083
- }
1084
- const key = arg.slice(2);
1085
- const value = args[index + 1];
1086
- if (value === undefined || value.startsWith("--")) {
1087
- options.set(key, "true");
1088
- continue;
1089
- }
1090
- options.set(key, value);
1091
- index += 1;
1092
- }
1093
- return options;
1094
- }
1095
-
1078
+ const options = new Map();
1079
+ for (let index = 0; index < args.length; index += 1) {
1080
+ const arg = args[index];
1081
+ if (!arg.startsWith("--")) {
1082
+ continue;
1083
+ }
1084
+ const key = arg.slice(2);
1085
+ const value = args[index + 1];
1086
+ if (value === undefined || value.startsWith("--")) {
1087
+ options.set(key, "true");
1088
+ continue;
1089
+ }
1090
+ options.set(key, value);
1091
+ index += 1;
1092
+ }
1093
+ return options;
1094
+ }
1095
+
1096
1096
  function formatList(values) {
1097
1097
  return values.length === 0 ? "none" : values.map((value) => `\`${value}\``).join(", ");
1098
1098
  }
@@ -1105,7 +1105,7 @@ function readCsv(value) {
1105
1105
  if (value === undefined || value.trim() === "") {
1106
1106
  return [];
1107
1107
  }
1108
- return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
1108
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
1109
1109
  }
1110
1110
 
1111
1111
  function renderImportMerge(result) {
@@ -1516,7 +1516,6 @@ function helpText() {
1516
1516
  "AI/automation operations:",
1517
1517
  " setup [--yes] [--agent codex[,claude,opencode,hermes]]",
1518
1518
  " import-skill --from <agent> --to <agent> --skill <id-or-dir> [--target-skill <id-or-dir>] [--adapted-file <path>] [--dry-run] [--yes] [--replace] [--json]",
1519
- " init [--dir <path>] [--scan-root <dir>[,<dir>]] [--no-scan-installed]",
1520
1519
  " uninstall [--dir <path>] [--dry-run] [--keep-settings] [--purge] [--remove-config|--reset-config] [--remove-reports] [--remove-hooks] [--keep-empty-dirs] [--agent-layer] [--agent codex[,claude,opencode,hermes]]",
1521
1520
  " inventory refresh [--dir <path>] [--config <path>] [--scan-root <dir>[,<dir>]] [--dry-run] [--json]",
1522
1521
  " inventory detect --unit <id> --config <path> [--install-output <path>] [--config-file a,b] [--source <value>] [--kind <kind>] [--scope <scope>] [--dry-run] [--json]",
@@ -1556,6 +1555,10 @@ function helpText() {
1556
1555
  " reconcile --config <path> --skills <dir> [--actual-harnesses a,b] [--out <path>]",
1557
1556
  " impact disable <skill-id> --config <path> --skills <dir> [--out <path>] [--json]",
1558
1557
  "",
1558
+ "Legacy project policy mode:",
1559
+ " init [--dir <path>] [--scan-root <dir>[,<dir>]] [--no-scan-installed]",
1560
+ " Deprecated project-local policy bootstrap; not needed for normal use.",
1561
+ "",
1559
1562
  "AI/automation control loop:",
1560
1563
  " Goal: keep skills broadly available while routing overlaps consistently; see docs/ai-skill-routing-goal.md.",
1561
1564
  " Development loop: observe → route → work → explain briefly → ask after → remember policy.",
@@ -1618,8 +1621,9 @@ function initHelpText() {
1618
1621
  return [
1619
1622
  "Usage: skillboard init [--dir <path>] [--scan-root <dir>[,<dir>]] [--no-scan-installed]",
1620
1623
  "",
1621
- "Creates the local SkillBoard control files for a project.",
1622
- "Use it once per project before asking the AI which skills it can use.",
1624
+ "Deprecated project-local policy bootstrap. This command is not needed for normal use.",
1625
+ "Normal flow: install agent-skillboard globally, let postinstall/setup refresh agent-layer guidance, then ask your AI normal work requests.",
1626
+ "Use init only for an existing workspace that intentionally maintains local SkillBoard policy files.",
1623
1627
  "",
1624
1628
  "Options:",
1625
1629
  " --dir <path> Project root to initialize; defaults to the current directory.",
@@ -1631,7 +1635,7 @@ function initHelpText() {
1631
1635
  " Imports trusted user-local skills as on-request skills.",
1632
1636
  " Keeps runtime, plugin, system, and external skills behind source review before manual activation.",
1633
1637
  "",
1634
- "Next:",
1638
+ "Legacy next steps:",
1635
1639
  " Run skillboard doctor --summary.",
1636
1640
  " Ask your AI: \"What skills can you use in this project?\"",
1637
1641
  ""
@@ -1645,7 +1649,7 @@ function setupHelpText() {
1645
1649
  "Installs or refreshes SkillBoard at the agent layer, not into a project.",
1646
1650
  "A normal global package install already runs this automatically for detected supported agents.",
1647
1651
  "Use setup later after adding another supported agent, enabling a new agent home, or skipping install scripts.",
1648
- "You do not need skillboard init for this install-time setup; init is only for a workspace where you want project-local policy files.",
1652
+ "skillboard init is deprecated project-local policy bootstrap and is not needed for normal use.",
1649
1653
  "Without --yes, setup explains the user agent skill files it will write and asks before installing when run in a TTY.",
1650
1654
  "In non-interactive automation, rerun with --yes after choosing the target agents.",
1651
1655
  "",
@@ -1736,11 +1740,11 @@ function doctorHelpText() {
1736
1740
  return [
1737
1741
  "Usage: skillboard doctor [--dir <path>] [--config <path>] [--skills <dir>] [--verify] [--strict] [--summary] [--json]",
1738
1742
  "",
1739
- "Checks whether a SkillBoard project is ready to use.",
1743
+ "Checks legacy local policy workspace health and source-review readiness.",
1740
1744
  "The status command is an alias for doctor.",
1741
1745
  "",
1742
1746
  "Options:",
1743
- " --dir <path> Project root to check; defaults to the current directory.",
1747
+ " --dir <path> Workspace root to check; defaults to the current directory.",
1744
1748
  " --config <path> Use a specific skillboard.config.yaml.",
1745
1749
  " --skills <dir> Use a specific skills directory.",
1746
1750
  " --verify Verify local source/cache digests when available.",
@@ -1748,7 +1752,8 @@ function doctorHelpText() {
1748
1752
  " --summary Print a short human summary.",
1749
1753
  " --json Print an agent-readable payload.",
1750
1754
  "",
1751
- "Use this after init and before trusting new skill sources.",
1755
+ "Use this for existing workspaces that intentionally keep local SkillBoard policy files.",
1756
+ "It is read-only and is not part of agent-layer setup.",
1752
1757
  ""
1753
1758
  ].join("\n");
1754
1759
  }
package/src/doctor.mjs CHANGED
@@ -121,10 +121,10 @@ function finalizeDoctor(result, recommendations) {
121
121
  function doctorRecommendations(result) {
122
122
  const recommendations = [];
123
123
  if (!result.bridges.some((bridge) => bridge.status === "installed")) {
124
- recommendations.push("run skillboard init to install agent bridge blocks");
124
+ recommendations.push("legacy project bridge blocks are absent; run skillboard init only if maintaining deprecated project-local policy");
125
125
  }
126
126
  if (result.bridges.some((bridge) => bridge.status === "unmanaged")) {
127
- recommendations.push("run skillboard init to add SkillBoard bridge blocks to unmanaged agent files");
127
+ recommendations.push("legacy project bridge is unmanaged; run skillboard init only if maintaining deprecated project-local policy");
128
128
  }
129
129
  if (result.bridges.some((bridge) => bridge.status === "broken")) {
130
130
  recommendations.push("repair AGENTS.md or CLAUDE.md SkillBoard bridge markers");