@mmerterden/multi-agent-pipeline 15.1.0 → 15.2.0

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 CHANGED
@@ -16,6 +16,23 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [15.2.0] - 2026-08-15
20
+
21
+ ### Added
22
+
23
+ - **`/multi-agent:update` auto-enables usage logging when a token is onboarded.**
24
+ The private dashboard stays opt-in and off by default, but once the shared
25
+ ingest token exists on a machine, an update flips `usageLog.enabled` on so the
26
+ admin's trusted group does not each have to toggle it by hand. It NEVER
27
+ fabricates or ships a secret: it activates only when a token resolves from
28
+ `MULTI_AGENT_USAGE_TOKEN`, `usageLog.token`, or the Keychain item named by the
29
+ new generic `keychainMapping.usage_ingest` slot - a machine that was never
30
+ given the token stays silent.
31
+ - **`usage-report.mjs` reads the ingest token from the Keychain** (via
32
+ `credential-store.sh`, resolved through `keychainMapping.usage_ingest`) as a
33
+ fallback after env and `usageLog.token`, so the secret never has to live in a
34
+ synced or plaintext file for logging to work.
35
+
19
36
  ## [15.1.0] - 2026-08-14
20
37
 
21
38
  Opt-in run telemetry to a private dashboard, and a refactor band that mines it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "15.1.0",
3
+ "version": "15.2.0",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -71,6 +71,33 @@ Update the pipeline in one command. Existing preferences are preserved; only ski
71
71
  fi
72
72
  ```
73
73
 
74
+ 5b. **Auto-enable usage logging when a token is already onboarded.** The private
75
+ usage dashboard is opt-in and off by default. This step flips it ON only when a
76
+ token can be resolved - it NEVER fabricates a secret or ships one, so a machine
77
+ that was never given the token stays silent. Resolution order: env
78
+ `MULTI_AGENT_USAGE_TOKEN`, then `usageLog.token`, then the Keychain item named by
79
+ `keychainMapping.usage_ingest`. Endpoint is left to the emitter's default.
80
+ ```bash
81
+ PREFS="$HOME/.claude/multi-agent-preferences.json"
82
+ if [ -f "$PREFS" ] && command -v jq >/dev/null 2>&1; then
83
+ ENABLED=$(jq -r '.global.usageLog.enabled // false' "$PREFS" 2>/dev/null)
84
+ if [ "$ENABLED" != "true" ]; then
85
+ TOK="${MULTI_AGENT_USAGE_TOKEN:-}"
86
+ [ -z "$TOK" ] && TOK=$(jq -r '.global.usageLog.token // empty' "$PREFS" 2>/dev/null)
87
+ if [ -z "$TOK" ]; then
88
+ KNAME=$(jq -r '.global.keychainMapping.usage_ingest // empty' "$PREFS" 2>/dev/null)
89
+ [ -n "$KNAME" ] && TOK=$(bash "$HOME/.claude/lib/credential-store.sh" get "$KNAME" 2>/dev/null)
90
+ fi
91
+ if [ -n "$TOK" ]; then
92
+ node -e 'const fs=require("fs"),p=process.argv[1];const j=JSON.parse(fs.readFileSync(p,"utf8"));j.global=j.global||{};j.global.usageLog=j.global.usageLog||{};j.global.usageLog.enabled=true;fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' "$PREFS"
93
+ echo " -> usage logging activated (ingest token found)"
94
+ else
95
+ echo " -> usage logging left off (no ingest token onboarded)"
96
+ fi
97
+ fi
98
+ fi
99
+ ```
100
+
74
101
  6. **Show the new version**:
75
102
  ```bash
76
103
  NEW_VERSION=$(node -p "require('$PIPE_DIR/package.json').version" 2>/dev/null || echo "unknown")
@@ -12,6 +12,7 @@
12
12
  "figma_mcp": null,
13
13
  "fortify": null,
14
14
  "graylog": null,
15
+ "usage_ingest": null,
15
16
  "firebase": null,
16
17
  "jenkins": null
17
18
  },
@@ -188,6 +188,13 @@
188
188
  "null"
189
189
  ]
190
190
  },
191
+ "usage_ingest": {
192
+ "type": [
193
+ "string",
194
+ "null"
195
+ ],
196
+ "description": "Keychain item holding the shared ingest token for the private usage dashboard. When set, usage-report.mjs reads the token from here (never from a synced file), and /multi-agent:update auto-enables usageLog once the token is onboarded. Absent = usage logging stays off."
197
+ },
191
198
  "figma_pat": {
192
199
  "type": [
193
200
  "string",
@@ -32,6 +32,7 @@ import { readFileSync, existsSync, readdirSync, appendFileSync } from "fs";
32
32
  import { homedir, platform } from "os";
33
33
  import { dirname, join } from "path";
34
34
  import { fileURLToPath } from "url";
35
+ import { execFileSync } from "child_process";
35
36
  import { costUsd } from "./_cost.mjs";
36
37
 
37
38
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -70,6 +71,36 @@ function resolvePrefs() {
70
71
  return resolveGlobalPrefs().usageLog ?? {};
71
72
  }
72
73
 
74
+ /**
75
+ * Resolve the ingest token without ever storing it in a synced file.
76
+ * Order: env, then prefs.usageLog.token, then the Keychain entry mapped by
77
+ * prefs.global.keychainMapping.usage_ingest (via credential-store.sh). This
78
+ * lets update flip usageLog.enabled on while the secret stays in the Keychain.
79
+ */
80
+ function resolveToken() {
81
+ if (process.env.MULTI_AGENT_USAGE_TOKEN) return process.env.MULTI_AGENT_USAGE_TOKEN;
82
+ const g = resolveGlobalPrefs();
83
+ const inline = g.usageLog?.token;
84
+ if (typeof inline === "string" && inline.trim()) return inline.trim();
85
+ const name = g.keychainMapping?.usage_ingest;
86
+ if (typeof name === "string" && name.trim()) {
87
+ const store = join(homedir(), ".claude", "lib", "credential-store.sh");
88
+ if (existsSync(store)) {
89
+ try {
90
+ const out = execFileSync("bash", [store, "get", name.trim()], {
91
+ encoding: "utf-8",
92
+ timeout: 4000,
93
+ stdio: ["ignore", "pipe", "ignore"],
94
+ }).trim();
95
+ if (out) return out;
96
+ } catch {
97
+ /* keychain unavailable - stay silent */
98
+ }
99
+ }
100
+ }
101
+ return "";
102
+ }
103
+
73
104
  function enabledPlugins(state) {
74
105
  const root = state.projectRoot || process.cwd();
75
106
  const settings = readJson(join(root, ".claude", "settings.json"));
@@ -393,7 +424,7 @@ async function post(endpoint, token, event) {
393
424
  async function main() {
394
425
  const args = parseArgs(process.argv.slice(2));
395
426
  const prefs = resolvePrefs();
396
- const token = process.env.MULTI_AGENT_USAGE_TOKEN || prefs.token || "";
427
+ const token = resolveToken();
397
428
  const endpoint = prefs.endpoint || ENDPOINT_DEFAULT;
398
429
 
399
430
  if (!args.dryRun && (prefs.enabled !== true || !token)) return;