roster-cli 0.3.0 → 0.3.1

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/README.md CHANGED
@@ -155,10 +155,10 @@ Once installed:
155
155
  (`ROSTER_DRIFT_DISABLE=1` to opt out). By default it watches
156
156
  `.claude/agents` plus, in a plugin-layout repo (one with a top-level
157
157
  `.claude-plugin/plugin.json`), the root `agents/` dir; override with
158
- `ROSTER_DRIFT_DIR` (colon-separated dir list). The advisory goes to both
159
- Claude's session context (so it can offer an audit proactively) and
160
- stderr (so you see it in the terminal). Advisory only — never blocks a
161
- session.
158
+ `ROSTER_DRIFT_DIR` (colon-separated dir list). The advisory is injected
159
+ into Claude's session context along with a relay directive, so Claude
160
+ surfaces it to you in its first response of the session. Advisory only —
161
+ never blocks a session.
162
162
 
163
163
  ## Contributing
164
164
 
@@ -145,7 +145,8 @@ export const pluginCacheSource = {
145
145
  const sourceLabel = `plugin:${plugin.name}@${plugin.version}`;
146
146
  return Promise.all(files.map(async (filePath) => {
147
147
  const raw = await readFile(filePath, 'utf8');
148
- return parseAgentMarkdown(raw, filePath, sourceLabel);
148
+ const agent = parseAgentMarkdown(raw, filePath, sourceLabel);
149
+ return { ...agent, pluginName: plugin.name };
149
150
  }));
150
151
  }));
151
152
  return agentLists.flat();
package/dist/usage.js CHANGED
@@ -64,13 +64,24 @@ function parseArgs(argv) {
64
64
  }
65
65
  /**
66
66
  * Pure join between observed invocation counts and a roster's agent names.
67
- * - unused: in the roster, but zero observed invocations.
68
- * - ghosts: observed invocations for a subagent_type that isn't in the roster.
67
+ * - unused: in the roster, but zero observed invocations (directly, or via an
68
+ * alias key such as the `<plugin>:<name>` form transcripts record for
69
+ * plugin-sourced subagents).
70
+ * - ghosts: observed invocations for a subagent_type that isn't in the roster
71
+ * and isn't a known alias of a roster name.
72
+ *
73
+ * `aliases` maps an observed count key (e.g. `sshworld:implementor`) to the
74
+ * canonical bare roster name (e.g. `implementor`). Omitting it preserves the
75
+ * prior exact-match-only behavior.
69
76
  */
70
- export function computeJoin(counts, rosterNames) {
77
+ export function computeJoin(counts, rosterNames, aliases = {}) {
71
78
  const rosterSet = new Set(rosterNames);
72
- const unused = rosterNames.filter((name) => !counts[name]);
73
- const ghosts = Object.keys(counts).filter((name) => !rosterSet.has(name));
79
+ const unused = rosterNames.filter((name) => {
80
+ if (counts[name])
81
+ return false;
82
+ return !Object.keys(counts).some((key) => aliases[key] === name);
83
+ });
84
+ const ghosts = Object.keys(counts).filter((name) => !rosterSet.has(name) && !(name in aliases && rosterSet.has(aliases[name])));
74
85
  return { unused, ghosts };
75
86
  }
76
87
  async function findJsonlFiles(root) {
@@ -148,6 +159,7 @@ async function countAgentInvocations(filePath, cutoff, counts) {
148
159
  }
149
160
  async function loadRosterNames(user, plugin) {
150
161
  const names = new Set();
162
+ const aliases = {};
151
163
  if (user) {
152
164
  const agents = await sources.user.load();
153
165
  for (const agent of agents)
@@ -155,10 +167,14 @@ async function loadRosterNames(user, plugin) {
155
167
  }
156
168
  if (plugin) {
157
169
  const agents = await sources['plugin-cache'].load();
158
- for (const agent of agents)
170
+ for (const agent of agents) {
159
171
  names.add(agent.name);
172
+ if (agent.pluginName) {
173
+ aliases[`${agent.pluginName}:${agent.name}`] = agent.name;
174
+ }
175
+ }
160
176
  }
161
- return [...names];
177
+ return { names: [...names], aliases };
162
178
  }
163
179
  function renderHuman(days, counts, unused, ghosts) {
164
180
  const lines = [];
@@ -209,8 +225,8 @@ export async function run(argv) {
209
225
  let unused = [];
210
226
  let ghosts = [];
211
227
  if (parsed.user || parsed.plugin) {
212
- const rosterNames = await loadRosterNames(parsed.user, parsed.plugin);
213
- const joined = computeJoin(counts, rosterNames);
228
+ const { names: rosterNames, aliases } = await loadRosterNames(parsed.user, parsed.plugin);
229
+ const joined = computeJoin(counts, rosterNames, aliases);
214
230
  unused = joined.unused;
215
231
  ghosts = joined.ghosts;
216
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roster-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Zero-runtime-dep static auditor for Claude Code agent rosters \u2014 overlap, harness gaps, routing, cost.",
5
5
  "type": "module",
6
6
  "bin": {