@mmerterden/multi-agent-pipeline 16.20.0 → 16.23.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +231 -102
  2. package/README.md +6 -8
  3. package/README.tr.md +6 -8
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/features.md +1 -0
  7. package/install/templates/claude-hooks.json +12 -1
  8. package/install/templates/copilot-instructions.md +17 -2
  9. package/package.json +1 -1
  10. package/pipeline/agents/bulk-reader.md +57 -0
  11. package/pipeline/commands/multi-agent/SKILL.md +0 -5
  12. package/pipeline/commands/multi-agent/help/SKILL.md +0 -10
  13. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +2 -2
  14. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
  15. package/pipeline/commands/multi-agent/resume-local/SKILL.md +2 -2
  16. package/pipeline/commands/multi-agent/setup/SKILL.md +7 -5
  17. package/pipeline/commands/multi-agent/sync/SKILL.md +13 -13
  18. package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -12
  19. package/pipeline/multi-agent-refs/phases/modes.md +1 -1
  20. package/pipeline/multi-agent-refs/phases/phase-0-init.md +7 -2
  21. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -1
  22. package/pipeline/multi-agent-refs/phases/phase-7-report.md +8 -1
  23. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  24. package/pipeline/multi-agent-refs/tracker-contract.md +46 -0
  25. package/pipeline/schemas/agent-state.schema.json +1 -1
  26. package/pipeline/schemas/bulk-read-output.schema.json +52 -0
  27. package/pipeline/schemas/prefs.schema.json +74 -19
  28. package/pipeline/schemas/token-budget.json +3 -3
  29. package/pipeline/scripts/bulk-read.sh +277 -0
  30. package/pipeline/scripts/check-read-size.py +335 -0
  31. package/pipeline/scripts/check-read-size.sh +86 -0
  32. package/pipeline/scripts/phase-tracker.sh +245 -3
  33. package/pipeline/scripts/pre-commit-check.sh +1 -0
  34. package/pipeline/scripts/uninstall.mjs +1 -0
  35. package/pipeline/skills/.skill-manifest.json +8 -24
  36. package/pipeline/skills/.skills-index.json +2 -46
  37. package/pipeline/skills/shared/README.md +4 -8
  38. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +0 -8
  39. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +1 -1
  40. package/pipeline/skills/shared/core/multi-agent-resume-local/SKILL.md +1 -1
  41. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +12 -12
  42. package/pipeline/skills/shared/external/backlog/SKILL.md +10 -6
  43. package/pipeline/skills/skills-index.md +2 -6
  44. package/pipeline/commands/multi-agent/dev/SKILL.md +0 -17
  45. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +0 -23
  46. package/pipeline/commands/multi-agent/dev-local/SKILL.md +0 -17
  47. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +0 -21
  48. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +0 -19
  49. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +0 -25
  50. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +0 -19
  51. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +0 -23
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ # check-read-size.sh - PreToolUse read gate for the multi-agent pipeline.
3
+ #
4
+ # The third deterministic hook, and the first on the READ side. The other two
5
+ # (pre-commit-check, agent-guard) inspect what the run writes; this one inspects
6
+ # what it pays to read.
7
+ #
8
+ # The bill it exists for: a phase that reads six 900-line files pays for 5,400
9
+ # lines at that phase's own rung, and the part it needed was a handful of
10
+ # symbols. `offload-ref.sh` already took the other half of that bill - the build
11
+ # log, the diff, the test output - by parking the payload and printing a pointer.
12
+ # Nothing looked at file reads, because nothing could: only a hook sees a tool
13
+ # call before it runs.
14
+ #
15
+ # Contract (Claude Code PreToolUse hook):
16
+ # - Reads the tool-call JSON on stdin.
17
+ # - Exit 2 -> BLOCK (reason on stderr, shown to the model).
18
+ # - Exit 0 -> allow.
19
+ #
20
+ # Modes - `prefs.global.bulkRead.mode`:
21
+ # off (default) nothing is inspected; the hook is a no-op.
22
+ # observe decide and LOG, never block. This is the measurement: run the
23
+ # pipeline normally and metrics.jsonl carries the read-size
24
+ # distribution, so the saving can be estimated before anything is
25
+ # built on top of it.
26
+ # enforce block a whole-file read over `minLines` and tell the model to
27
+ # delegate it to bulk-read.sh instead.
28
+ #
29
+ # Why observe is a real mode and not a debug flag: the honest order is measure,
30
+ # then route. A gate switched straight to enforce has no baseline to be
31
+ # compared against, and "we cut tokens" becomes a claim nothing can check.
32
+ #
33
+ # Why the development phase is exempt by default: in Claude Code, `Edit` requires
34
+ # the same file to have been read first. A gate that blocks reads in Phase 3
35
+ # blocks editing. The gate is for the phases that read to UNDERSTAND.
36
+ #
37
+ # Safety design (fail-OPEN): any internal error, missing python3, missing helper,
38
+ # empty payload -> exit 0. It never executes the inspected command, never writes
39
+ # to the inspected file, makes no network call, and prints no file content.
40
+
41
+ set -u
42
+
43
+ HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd || true)"
44
+ HELPER="$HERE/check-read-size.py"
45
+ [ -f "$HELPER" ] || exit 0
46
+ command -v python3 >/dev/null 2>&1 || exit 0
47
+
48
+ PAYLOAD="$(cat 2>/dev/null || true)"
49
+ [ -z "$PAYLOAD" ] && exit 0
50
+
51
+ DECISION="$(printf '%s' "$PAYLOAD" | python3 "$HELPER" 2>/dev/null || true)"
52
+ [ -z "$DECISION" ] && exit 0
53
+
54
+ VERDICT="$(printf '%s' "$DECISION" | cut -f1)"
55
+ FILE="$(printf '%s' "$DECISION" | cut -f2)"
56
+ LINES="$(printf '%s' "$DECISION" | cut -f3)"
57
+ WHY="$(printf '%s' "$DECISION" | cut -f4)"
58
+
59
+ # Telemetry is best-effort and never gates the decision. `log-metric.sh` already
60
+ # tolerates an unwritable directory, so the only guard needed here is the
61
+ # script's own presence.
62
+ note() {
63
+ [ -x "$HERE/log-metric.sh" ] || return 0
64
+ "$HERE/log-metric.sh" "${MULTI_AGENT_TASK_ID:-unknown}" "${MULTI_AGENT_PHASE:-0}" \
65
+ "read.$1" lines="${LINES:-0}" reason="${WHY:-}" >/dev/null 2>&1 || true
66
+ }
67
+
68
+ case "$VERDICT" in
69
+ OBSERVE)
70
+ note observed
71
+ exit 0 ;;
72
+ BLOCK)
73
+ note blocked
74
+ echo "BLOCKED by check-read-size: ${FILE} is ${LINES} lines." >&2
75
+ echo "Reading it whole costs this phase's rung for a file whose useful part is a few symbols." >&2
76
+ echo "" >&2
77
+ echo "Delegate it, then work from what comes back:" >&2
78
+ echo " bash \$HOME/.claude/scripts/bulk-read.sh --file '${FILE}' --question '<what you need from it>'" >&2
79
+ echo "" >&2
80
+ echo "The summary carries LINE NUMBERS, so a follow-up Read with an offset/limit around the" >&2
81
+ echo "region you actually need is the intended next step and passes this gate." >&2
82
+ echo "Full text stays on disk under .multi-agent/refs/ if the summary is not enough." >&2
83
+ exit 2 ;;
84
+ *)
85
+ exit 0 ;;
86
+ esac
@@ -64,6 +64,8 @@ usage:
64
64
  phase-tracker.sh meta <phase_id> <key> <value>
65
65
  phase-tracker.sh now <phase_id> "<text>"
66
66
  phase-tracker.sh cost <phase_id>|total
67
+ phase-tracker.sh tiles
68
+ phase-tracker.sh report
67
69
  phase-tracker.sh render
68
70
  USAGE
69
71
  exit 64
@@ -498,6 +500,167 @@ release_state_lock() {
498
500
  # Never leave a lock behind if a mutating action dies mid-critical-section.
499
501
  trap release_state_lock EXIT
500
502
 
503
+ # The cost table is keyed by family ("opus", "sonnet", "gpt-5.6"), but the name
504
+ # an agent has at hand is the full id ("claude-opus-5"). Storing the id verbatim
505
+ # priced every phase at "-" while the tracker looked perfectly healthy, so the
506
+ # family is resolved here, once, and an unresolvable name is said out loud
507
+ # instead of quietly costing nothing.
508
+ normalize_model() {
509
+ local raw="$1" lower
510
+ lower=$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')
511
+ case "$lower" in
512
+ *terra*) printf 'gpt-5.6-terra' ;;
513
+ *gpt-5.6* | *gpt5.6*) printf 'gpt-5.6' ;;
514
+ *gpt-5.4* | *gpt5.4*) printf 'gpt-5.4' ;;
515
+ *opus*) printf 'opus' ;;
516
+ *sonnet*) printf 'sonnet' ;;
517
+ *haiku*) printf 'haiku' ;;
518
+ *fable*) printf 'fable' ;;
519
+ *) printf '%s' "$raw" ;;
520
+ esac
521
+ }
522
+
523
+ # Which CLI tree this copy runs from. The card below is identical on all three
524
+ # hosts, but the WIDGET is not: Claude Code has TaskCreate/TaskUpdate, Codex has
525
+ # update_plan, Copilot CLI has no task UI at all. Every "what to call next" hint
526
+ # has to know where it is, or it names a tool the host does not have.
527
+ SELF_PATH="${BASH_SOURCE[0]:-$0}"
528
+ host_kind() {
529
+ case "${MULTI_AGENT_HOST:-}" in
530
+ claude | codex | copilot)
531
+ printf '%s' "$MULTI_AGENT_HOST"
532
+ return 0
533
+ ;;
534
+ esac
535
+ case "$SELF_PATH" in
536
+ */.codex/*) printf 'codex' ;;
537
+ */.copilot/*) printf 'copilot' ;;
538
+ *) printf 'claude' ;;
539
+ esac
540
+ }
541
+
542
+ # The completion line the contract requires, built from what was actually
543
+ # recorded. Phases with no recorded spend say so rather than printing zeros,
544
+ # which is the difference between "cost unknown" and "cost was nothing".
545
+ narration_line() {
546
+ local pid="$1" state name idx model t_in t_out usd
547
+ state="$(load_state)"
548
+ name=$(echo "$state" | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .name) // ""')
549
+ idx=$(echo "$state" | jq -r --arg id "$pid" '[.phases[]?.id] | index($id) // "-"')
550
+ model=$(echo "$state" | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .model) // ""')
551
+ t_in=$(echo "$state" | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .tokens_in) // 0')
552
+ t_out=$(echo "$state" | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .tokens_out) // 0')
553
+ if [ "$t_in" -eq 0 ] && [ "$t_out" -eq 0 ]; then
554
+ printf 'Phase %s %s done (no LLM calls)' "$pid" "$name"
555
+ return 0
556
+ fi
557
+ usd="-"
558
+ [ "$idx" != "-" ] && usd=$(phase_usd "$idx")
559
+ printf 'Phase %s %s done - ~%s in / ~%s out tokens (%s, ~$%s)' \
560
+ "$pid" "$name" "$(format_tokens "$t_in")" "$(format_tokens "$t_out")" \
561
+ "${model:-unknown model}" "$usd"
562
+ }
563
+
564
+ # Printed after every status change. The card goes to stdout, and on every host
565
+ # that collapses tool output the user never sees it - so the hint names the
566
+ # host's own widget call, which the agent makes in its own turn where the user
567
+ # does see it.
568
+ tracker_next_hint() {
569
+ local pid="$1" status="$2" name mirror
570
+ [ "${TRACKER_QUIET:-0}" = "1" ] && return 0
571
+ name=$(load_state | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .name) // ""')
572
+ case "$(host_kind)" in
573
+ claude) mirror="TaskUpdate(\"Phase $pid $name\", status=\"$status\")" ;;
574
+ codex) mirror="update_plan: set step \"Phase $pid $name\" to $status (send the FULL step list, it is not a delta)" ;;
575
+ *) mirror="no task widget on this host - reprint the card above inside your reply text" ;;
576
+ esac
577
+ printf '\n-- NEXT (required) --\n %s\n' "$mirror"
578
+ case "$status" in
579
+ completed | failed)
580
+ printf ' narrate one line in outputLanguage: %s\n' "$(narration_line "$pid")"
581
+ ;;
582
+ esac
583
+ }
584
+
585
+ # Elapsed with an hours bucket. format_elapsed stops at minutes because the card
586
+ # shows one phase at a time; a whole-run total reads as "102m" without this.
587
+ format_span() {
588
+ local s="$1"
589
+ [ -z "$s" ] && { echo "-"; return; }
590
+ [ "$s" -lt 0 ] 2>/dev/null && { echo "-"; return; }
591
+ if [ "$s" -lt 3600 ]; then
592
+ format_elapsed "$s"
593
+ else
594
+ printf '%dh %dm' "$((s / 3600))" "$(((s % 3600) / 60))"
595
+ fi
596
+ }
597
+
598
+ # The end-of-run report: what the pipeline spent, phase by phase, and what it
599
+ # has to say about phases it could not price. Printed by Phase 7 next to the
600
+ # work summary, which covers what actually changed on disk.
601
+ report() {
602
+ need_jq
603
+ local state task_id started
604
+ state="$(load_state)"
605
+ task_id=$(echo "$state" | jq -r '.task_id // "?"')
606
+ started=$(echo "$state" | jq -r '.started_at // ""')
607
+
608
+ printf '\n== Run report: %s ==\n' "$task_id"
609
+ printf '%-22s %-11s %10s %-16s %-16s %8s\n' \
610
+ "Phase" "Status" "Elapsed" "Tokens in/out" "Model" "USD"
611
+ printf -- '%s\n' "----------------------------------------------------------------------------------"
612
+
613
+ local total_in=0 total_out=0 unpriced=""
614
+ local row
615
+ while IFS=$'\037' read -r idx id name status p_start p_end t_in t_out model no_llm; do
616
+ [ -n "$id" ] || continue
617
+ local secs="" span="-" toks="-" usd
618
+ if [ -n "$p_start" ]; then
619
+ local e_start e_end
620
+ e_start=$(iso_to_epoch "$p_start")
621
+ e_end=$([ -n "$p_end" ] && iso_to_epoch "$p_end" || now_epoch)
622
+ [ -n "$e_start" ] && [ -n "$e_end" ] && secs=$((e_end - e_start))
623
+ [ -n "$secs" ] && span=$(format_span "$secs")
624
+ fi
625
+ if [ "${t_in:-0}" -gt 0 ] || [ "${t_out:-0}" -gt 0 ]; then
626
+ toks="$(format_tokens "${t_in:-0}") / $(format_tokens "${t_out:-0}")"
627
+ total_in=$((total_in + t_in))
628
+ total_out=$((total_out + t_out))
629
+ elif [ "$status" = "completed" ] && [ "$no_llm" != "true" ]; then
630
+ unpriced="$unpriced $id"
631
+ fi
632
+ usd=$(phase_usd "$idx")
633
+ printf '%-22s %-11s %10s %-16s %-16s %8s\n' \
634
+ "$id $name" "$status" "$span" "$toks" "${model:--}" "$usd"
635
+ done <<EOF
636
+ $(echo "$state" | jq -r '
637
+ [.phases[]?] | to_entries[] |
638
+ [ (.key|tostring), .value.id, (.value.name // ""), (.value.status // "pending"),
639
+ (.value.started_at // ""), (.value.completed_at // ""),
640
+ ((.value.tokens_in // 0)|tostring), ((.value.tokens_out // 0)|tostring),
641
+ (.value.model // ""), ((.value.no_llm // false)|tostring) ] | join("\u001f")')
642
+ EOF
643
+
644
+ printf -- '%s\n' "----------------------------------------------------------------------------------"
645
+ local run_secs="" run_span="-"
646
+ if [ -n "$started" ]; then
647
+ local e_run last
648
+ e_run=$(iso_to_epoch "$started")
649
+ last=$(echo "$state" | jq -r '[.phases[]?.completed_at // empty] | max // ""')
650
+ local e_last
651
+ e_last=$([ -n "$last" ] && iso_to_epoch "$last" || now_epoch)
652
+ [ -n "$e_run" ] && [ -n "$e_last" ] && run_secs=$((e_last - e_run))
653
+ [ -n "$run_secs" ] && run_span=$(format_span "$run_secs")
654
+ fi
655
+ printf '%-22s %-11s %10s %-16s %-16s %8s\n' \
656
+ "Total" "" "$run_span" \
657
+ "$(format_tokens "$total_in") / $(format_tokens "$total_out")" "" "$(phase_usd total)"
658
+ if [ -n "$unpriced" ]; then
659
+ printf 'Cost unavailable - no tokens recorded for phase(s):%s\n' "$unpriced"
660
+ fi
661
+ printf '\n'
662
+ }
663
+
501
664
  render() {
502
665
  need_jq
503
666
  local state
@@ -762,12 +925,49 @@ case "$ACTION" in
762
925
 
763
926
  update)
764
927
  need_jq
765
- [ "$#" -ge 2 ] || { echo "update needs <phase_id> <status>" >&2; exit 64; }
766
- PID="$1"; STATUS="$2"
928
+ [ "$#" -ge 2 ] || { echo "update needs <phase_id> <status> [--no-llm]" >&2; exit 64; }
929
+ PID="$1"; STATUS="$2"; shift 2
930
+ NO_LLM=0
931
+ while [ "$#" -gt 0 ]; do
932
+ case "$1" in
933
+ --no-llm) NO_LLM=1 ;;
934
+ *) echo "update: unknown option $1" >&2; exit 64 ;;
935
+ esac
936
+ shift
937
+ done
767
938
  case "$STATUS" in
768
939
  pending|in_progress|completed|failed|skipped) ;;
769
940
  *) echo "bad status: $STATUS" >&2; exit 64 ;;
770
941
  esac
942
+ # Accounting gate. A phase that ran LLM work and recorded nothing produces a
943
+ # completion line with nothing to say and a cost breakdown that reads as
944
+ # unavailable - which is exactly how per-phase token reporting went missing
945
+ # for months while every gate stayed green, because the gates lint the docs
946
+ # for the CALL and nothing checks that the call happened. Refusing the
947
+ # completion is recoverable (record, then re-run) and loses no work; a
948
+ # phase that genuinely made no LLM call says so with --no-llm.
949
+ if [ "$STATUS" = "completed" ] && [ "$NO_LLM" -eq 0 ]; then
950
+ case " ${TRACKER_LLM_PHASES:-1 2 3 4} " in
951
+ *" $PID "*)
952
+ RECORDED=$(load_state | jq -r --arg id "$PID" \
953
+ '(.phases[] | select(.id == $id) | (.tokens_in // 0) + (.tokens_out // 0)) // 0')
954
+ if [ "${RECORDED:-0}" -eq 0 ]; then
955
+ cat >&2 <<GATE
956
+ update: phase $PID has no recorded spend, so its completion line would have
957
+ nothing to report and the run report would price it as unavailable.
958
+
959
+ Record it first, then re-run this update:
960
+ phase-tracker.sh model $PID <model_name>
961
+ phase-tracker.sh tokens $PID <input_count> <output_count> [cached_count]
962
+
963
+ If the phase genuinely made no LLM call, say so explicitly:
964
+ phase-tracker.sh update $PID completed --no-llm
965
+ GATE
966
+ exit 3
967
+ fi
968
+ ;;
969
+ esac
970
+ fi
771
971
  NOW_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
772
972
  acquire_state_lock
773
973
  state=$(load_state)
@@ -782,10 +982,14 @@ case "$ACTION" in
782
982
  else . end
783
983
  )
784
984
  ')
985
+ if [ "$NO_LLM" -eq 1 ]; then
986
+ new=$(echo "$new" | jq --arg id "$PID" '.phases |= map(if .id == $id then .no_llm = true else . end)')
987
+ fi
785
988
  save_state "$new"
786
989
  release_state_lock
787
990
  emit_otel_span "phase.update" "$PID" "$STATUS" "{\"status\": \"$STATUS\"}"
788
991
  render
992
+ tracker_next_hint "$PID" "$STATUS"
789
993
  usage_live_ping "$(basename "$TRACKER_DIR")" "$PID"
790
994
  ;;
791
995
 
@@ -893,7 +1097,11 @@ case "$ACTION" in
893
1097
  model)
894
1098
  need_jq
895
1099
  [ "$#" -ge 2 ] || { echo "model needs <phase_id> <model_name>" >&2; exit 64; }
896
- PID="$1"; MODEL="$2"
1100
+ PID="$1"; MODEL="$(normalize_model "$2")"
1101
+ if [ -f "$COST_TABLE" ] && ! jq -e --arg m "$MODEL" '.prices[$m]' "$COST_TABLE" >/dev/null 2>&1; then
1102
+ echo "phase-tracker: '$2' is not a priced model - this phase will report USD as '-'." >&2
1103
+ echo " priced names: $(jq -r '.prices | keys | join(", ")' "$COST_TABLE" 2>/dev/null)" >&2
1104
+ fi
897
1105
  acquire_state_lock
898
1106
  state=$(load_state)
899
1107
  new=$(echo "$state" | jq --arg id "$PID" --arg m "$MODEL" '
@@ -948,6 +1156,40 @@ case "$ACTION" in
948
1156
  fi
949
1157
  ;;
950
1158
 
1159
+ tiles)
1160
+ need_jq
1161
+ tiles_state=$(load_state)
1162
+ tiles_count=$(echo "$tiles_state" | jq '[.phases[]?] | length')
1163
+ [ "${tiles_count:-0}" -gt 0 ] || {
1164
+ echo "tiles: no phases registered - run 'add' for each phase first" >&2
1165
+ exit 64
1166
+ }
1167
+ case "$(host_kind)" in
1168
+ claude)
1169
+ echo "REQUIRED - create one native tile per phase, in this exact order,"
1170
+ echo "BEFORE any TaskUpdate. The widget renders by creation order, not by"
1171
+ echo "phase number, so an out-of-order call scrambles the stack."
1172
+ echo "$tiles_state" | jq -r '.phases[] | " TaskCreate(subject: \"Phase \(.id) \(.name)\")"'
1173
+ ;;
1174
+ codex)
1175
+ echo "REQUIRED - register the plan in ONE update_plan call with this step"
1176
+ echo "list. update_plan takes the full list, not a delta, so every later"
1177
+ echo "boundary resends it with one step's status changed."
1178
+ echo "$tiles_state" | jq -c '{plan: [.phases[] | {step: "Phase \(.id) \(.name)", status: "pending"}]}'
1179
+ ;;
1180
+ *)
1181
+ echo "Copilot CLI has no native task widget: the bordered card IS the widget."
1182
+ echo "Reprint it inside your reply at every phase boundary - tool output is"
1183
+ echo "collapsed, so a card left in stdout never reaches the user."
1184
+ render
1185
+ ;;
1186
+ esac
1187
+ ;;
1188
+
1189
+ report)
1190
+ report
1191
+ ;;
1192
+
951
1193
  render)
952
1194
  render
953
1195
  ;;
@@ -194,6 +194,7 @@ scan_file() {
194
194
  if (s ~ /^[0-9]+$/) next; # pure digits
195
195
  if (s ~ /^[0-9a-f]+$/) next; # lowercase hex (git sha / md5 / sha-*)
196
196
  if (s ~ /^[0-9A-F]+$/) next; # uppercase hex
197
+ if (s ~ /^([A-Z][a-z]+)+[A-Z]*$/) next; # CamelCase words only (label key / type name), never a credential
197
198
  delete freq;
198
199
  for (i = 1; i <= n; i++) { c = substr(s, i, 1); freq[c]++ }
199
200
  H = 0;
@@ -67,6 +67,7 @@ const HOME = process.env.HOME || process.env.USERPROFILE;
67
67
  export const PIPELINE_AGENT_FILES = [
68
68
  "android-architect.md",
69
69
  "backend-architect.md",
70
+ "bulk-reader.md",
70
71
  "code-reviewer.md",
71
72
  "dev-critic.md",
72
73
  "explorer.md",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-09-02T11:13:26Z",
4
- "skillCount": 211,
3
+ "generatedAt": "2026-09-08T16:31:56Z",
4
+ "skillCount": 207,
5
5
  "entries": [
6
6
  {
7
7
  "path": "shared/core/apple-archive-compliance/SKILL.md",
@@ -43,22 +43,6 @@
43
43
  "path": "shared/core/multi-agent-design-check/SKILL.md",
44
44
  "sha256": "bdf5ef1b8e0b27eb216eed89382ced99552c4aadfb61bc9a4931e41e8316d92a"
45
45
  },
46
- {
47
- "path": "shared/core/multi-agent-dev-autopilot/SKILL.md",
48
- "sha256": "42f8f5008a32202f7a51cec57d278baeaedca6d2ce70200dfce2fdd0eb62aaa4"
49
- },
50
- {
51
- "path": "shared/core/multi-agent-dev-local-autopilot/SKILL.md",
52
- "sha256": "257aa9d8ff48b76c07809d675f139087522edec598d5fb5bb0fcbd17e2857aac"
53
- },
54
- {
55
- "path": "shared/core/multi-agent-dev-local/SKILL.md",
56
- "sha256": "ba7e75447935e1c7bfc77bbbf8a0affe9cc2f631c2acea091de1b68d4bb34964"
57
- },
58
- {
59
- "path": "shared/core/multi-agent-dev/SKILL.md",
60
- "sha256": "4ac1401ebdd6d8ff21b85d11b1dffbd8399046348693eca758e6877a2ae07184"
61
- },
62
46
  {
63
47
  "path": "shared/core/multi-agent-diff-explain/SKILL.md",
64
48
  "sha256": "7cb37a224e61d66e350202ecfc5fbc6c33da3fe0718504a093602f5cde5b648f"
@@ -81,11 +65,11 @@
81
65
  },
82
66
  {
83
67
  "path": "shared/core/multi-agent-help/SKILL.md",
84
- "sha256": "5e4866b8ee203113b5db24e2d275323ae053254bbf677ad7f2f1e5c153fe1903"
68
+ "sha256": "87d45a04c782b03569b0ab9ee54da8414cbadd5a2a82a7701f38dc8a3ee94627"
85
69
  },
86
70
  {
87
71
  "path": "shared/core/multi-agent-ios-coding-standard/SKILL.md",
88
- "sha256": "447a06d1a15928bf4569ee95d67332194b0db48a383be67a50eb436377d71019"
72
+ "sha256": "15a8d583cb7c772d5223c66c158062f25fdff4c41c6d353b7b70590a441dea38"
89
73
  },
90
74
  {
91
75
  "path": "shared/core/multi-agent-issue/SKILL.md",
@@ -137,7 +121,7 @@
137
121
  },
138
122
  {
139
123
  "path": "shared/core/multi-agent-resume-local/SKILL.md",
140
- "sha256": "5b53ed0f6726082d0a4fb63f407578cd964df2f66ae3cc12f3f47fa3754fa50c"
124
+ "sha256": "a4bedf949ab648d977dd1409339d08af204341f555d69f71f58c1a54dffaff6d"
141
125
  },
142
126
  {
143
127
  "path": "shared/core/multi-agent-resume/SKILL.md",
@@ -193,11 +177,11 @@
193
177
  },
194
178
  {
195
179
  "path": "shared/core/multi-agent-store-ready/SKILL.md",
196
- "sha256": "7ee313ed5dd0cb5ecd94ab68f8079323581a9337939bf60abc367163cf89fc57"
180
+ "sha256": "0060d1d7a8ec1e8cce30975f125faf46e9e384d23100ae46e82c9f194a0bf9fa"
197
181
  },
198
182
  {
199
183
  "path": "shared/core/multi-agent-sync/SKILL.md",
200
- "sha256": "dc6f675aacbedc5c1516cbd7c624ee9cb17bb7c0136ed7effb66fe3cb401101d"
184
+ "sha256": "c0e2c22d0f6da9a60f5552d6f47db0490a355ea4ce08722716470e03535e5418"
201
185
  },
202
186
  {
203
187
  "path": "shared/core/multi-agent-test-accessibility/SKILL.md",
@@ -321,7 +305,7 @@
321
305
  },
322
306
  {
323
307
  "path": "shared/external/backlog/SKILL.md",
324
- "sha256": "662618cb69086184e62d037d6589b22a9c53dc9c91f1381475986b4e62cb07f6"
308
+ "sha256": "d6602ec8f7bf70a305f4590ace8e8b92e28b165ffda2efb2fd95c8c27d541e23"
325
309
  },
326
310
  {
327
311
  "path": "shared/external/callkit-voip/SKILL.md",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "skillCount": 211,
3
+ "skillCount": 207,
4
4
  "entries": [
5
5
  {
6
6
  "name": "accessibility-compliance-accessibility-audit",
@@ -1036,50 +1036,6 @@
1036
1036
  "triggerPaths": [],
1037
1037
  "relativePath": "shared/core/multi-agent-design-check/SKILL.md"
1038
1038
  },
1039
- {
1040
- "name": "multi-agent-dev",
1041
- "description": "Removed in v16.0.0. Depth is a question the run asks, not a command name: start /multi-agent and answer Short. Invoke only to see that redirect.",
1042
- "platform": null,
1043
- "group": "core",
1044
- "plugin": null,
1045
- "invokeAs": "multi-agent-dev",
1046
- "triggerKeywords": [],
1047
- "triggerPaths": [],
1048
- "relativePath": "shared/core/multi-agent-dev/SKILL.md"
1049
- },
1050
- {
1051
- "name": "multi-agent-dev-autopilot",
1052
- "description": "Removed in v16.0.0 and not replaced: an unsupervised run is always the full pipeline now. Invoke only to be pointed at /multi-agent:autopilot or the interactive picker.",
1053
- "platform": null,
1054
- "group": "core",
1055
- "plugin": null,
1056
- "invokeAs": "multi-agent-dev-autopilot",
1057
- "triggerKeywords": [],
1058
- "triggerPaths": [],
1059
- "relativePath": "shared/core/multi-agent-dev-autopilot/SKILL.md"
1060
- },
1061
- {
1062
- "name": "multi-agent-dev-local",
1063
- "description": "Removed in v16.0.0. Its worktree-free twin is /multi-agent:local, which asks the same depth question; answer Short there. Invoke only to see that redirect.",
1064
- "platform": null,
1065
- "group": "core",
1066
- "plugin": null,
1067
- "invokeAs": "multi-agent-dev-local",
1068
- "triggerKeywords": [],
1069
- "triggerPaths": [],
1070
- "relativePath": "shared/core/multi-agent-dev-local/SKILL.md"
1071
- },
1072
- {
1073
- "name": "multi-agent-dev-local-autopilot",
1074
- "description": "Retired alongside its worktree twin in v16.0.0, with nothing standing in for it. Invoke only to be pointed at /multi-agent:local-autopilot or the interactive picker.",
1075
- "platform": null,
1076
- "group": "core",
1077
- "plugin": null,
1078
- "invokeAs": "multi-agent-dev-local-autopilot",
1079
- "triggerKeywords": [],
1080
- "triggerPaths": [],
1081
- "relativePath": "shared/core/multi-agent-dev-local-autopilot/SKILL.md"
1082
- },
1083
1039
  {
1084
1040
  "name": "multi-agent-diff-explain",
1085
1041
  "description": "Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which code change'. Use when a review finding has to be traced to the exact lines that caused it.",
@@ -1148,7 +1104,7 @@
1148
1104
  },
1149
1105
  {
1150
1106
  "name": "multi-agent-ios-coding-standard",
1151
- "description": "Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to dev/dev-local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions.",
1107
+ "description": "Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to /multi-agent or :local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions.",
1152
1108
  "platform": null,
1153
1109
  "group": "core",
1154
1110
  "plugin": null,
@@ -2,11 +2,11 @@
2
2
 
3
3
  Single source of truth for skills delivered to both Claude Code (`~/.claude/skills/`) and Copilot CLI (`~/.copilot/skills/`) by the installer.
4
4
 
5
- **Total:** 211 skills (58 core + 153 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
5
+ **Total:** 207 skills (54 core + 153 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
6
6
 
7
7
  ## Directory layout
8
8
 
9
- - **`core/`** - 58 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
9
+ - **`core/`** - 54 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
10
10
  - **`external/`** - 153 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
11
11
  - Install destinations (ADR-0009): Claude Code gets NO local copy of `external/` - it loads those skills from the `multi-agent-plugins` marketplace, namespaced (`ai-<stack>-toolkit:<name>`); only the two compliance catalogs from `core/` land in `~/.claude/skills/`. Copilot CLI and Codex CLI receive a flat copy filtered to the enabled stacks. `external/` remains the single authoring source that `build-stack-plugins.mjs` publishes from.
12
12
 
@@ -14,7 +14,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
14
14
 
15
15
  ## Categories
16
16
 
17
- - [Pipeline Orchestration](#pipeline-orchestration) - 58
17
+ - [Pipeline Orchestration](#pipeline-orchestration) - 54
18
18
  - [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 90
19
19
  - [Android / Kotlin](#android-kotlin) - 13
20
20
  - [Web](#web) - 10
@@ -36,17 +36,13 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
36
36
  | [`multi-agent-complaint-analysis`](./core/multi-agent-complaint-analysis/) | `core` | Customer-complaint triage. Ingests complaints (paste, csv/xlsx/txt/json file, Jira issue, Confluence URL), fetches Graylog evidence per trx/ |
37
37
  | [`multi-agent-create-jira`](./core/multi-agent-create-jira/) | `core` | Create a standards-compliant Jira issue (Task / Bug / Story): asks the type, mines project conventions, drafts from a standard template with |
38
38
  | [`multi-agent-design-check`](./core/multi-agent-design-check/) | `core` | Mock-mode vs Figma design audit (iOS / Android, local-only). Pick repo + module, gate on mock support, enumerate every state driver into a c |
39
- | [`multi-agent-dev`](./core/multi-agent-dev/) | `core` | Removed in v16.0.0. Depth is a question the run asks, not a command name: start /multi-agent and answer Short. Invoke only to see that redir |
40
- | [`multi-agent-dev-autopilot`](./core/multi-agent-dev-autopilot/) | `core` | Removed in v16.0.0 and not replaced: an unsupervised run is always the full pipeline now. Invoke only to be pointed at /multi-agent:autopilo |
41
- | [`multi-agent-dev-local`](./core/multi-agent-dev-local/) | `core` | Removed in v16.0.0. Its worktree-free twin is /multi-agent:local, which asks the same depth question; answer Short there. Invoke only to see |
42
- | [`multi-agent-dev-local-autopilot`](./core/multi-agent-dev-local-autopilot/) | `core` | Retired alongside its worktree twin in v16.0.0, with nothing standing in for it. Invoke only to be pointed at /multi-agent:local-autopilot o |
43
39
  | [`multi-agent-diff-explain`](./core/multi-agent-diff-explain/) | `core` | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
44
40
  | [`multi-agent-feedback`](./core/multi-agent-feedback/) | `core` | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
45
41
  | [`multi-agent-forget`](./core/multi-agent-forget/) | `core` | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
46
42
  | [`multi-agent-garbage-collect`](./core/multi-agent-garbage-collect/) | `core` | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
47
43
  | [`multi-agent-graph`](./core/multi-agent-graph/) | `core` | Build and query this repo's code graph: a deterministic, LLM-free map of symbols, imports and references used to narrow Phase 1's Explore sc |
48
44
  | [`multi-agent-help`](./core/multi-agent-help/) | `core` | Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatib |
49
- | [`multi-agent-ios-coding-standard`](./core/multi-agent-ios-coding-standard/) | `core` | Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to dev/dev-l |
45
+ | [`multi-agent-ios-coding-standard`](./core/multi-agent-ios-coding-standard/) | `core` | Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to /multi-ag |
50
46
  | [`multi-agent-issue`](./core/multi-agent-issue/) | `core` | List unassigned GitHub issues, pick one, auto-assign, and launch the multi-agent pipeline. Use when a GitHub issue should be picked up and s |
51
47
  | [`multi-agent-jira`](./core/multi-agent-jira/) | `core` | List open Jira issues, pick one, and launch the multi-agent pipeline. Use when a Jira issue should be picked up and started without knowing |
52
48
  | [`multi-agent-kill`](./core/multi-agent-kill/) | `core` | Stop the given task, then remove its worktree and branch. Asks for confirmation. Use when a running or stuck task should be stopped and its |
@@ -87,10 +87,6 @@ Modes:
87
87
  Dedicated dash commands (Copilot):
88
88
  multi-agent-local, multi-agent-autopilot, multi-agent-local-autopilot
89
89
 
90
- Removed in v16.0.0: multi-agent-dev -> multi-agent + Short;
91
- multi-agent-dev-local -> multi-agent-local + Short; the two dev autopilot
92
- names have no equivalent (fast-plus-unattended no longer exists).
93
-
94
90
  ------------------------------------------------------------
95
91
 
96
92
  Utility Commands (dash form on Copilot, colon form on Claude Code):
@@ -264,10 +260,6 @@ Modlar:
264
260
  Dedicated dash komutlar (Copilot):
265
261
  multi-agent-local, multi-agent-autopilot, multi-agent-local-autopilot
266
262
 
267
- v16.0.0'da kaldırıldı: multi-agent-dev -> multi-agent + Kısa;
268
- multi-agent-dev-local -> multi-agent-local + Kısa; iki dev autopilot adının
269
- birebir karşılığı yok (hızlı+gözetimsiz artık yok).
270
-
271
263
  ------------------------------------------------------------
272
264
 
273
265
  Utility Komutları:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: multi-agent-ios-coding-standard
3
3
  language: en
4
- description: "Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to dev/dev-local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions."
4
+ description: "Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to /multi-agent or :local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions."
5
5
  user-invocable: true
6
6
  argument-hint: "[module name or path]"
7
7
  ---
@@ -23,7 +23,7 @@ Phases 1-3 (Analysis / Planning / Dev) are skipped by design - the branch's lo
23
23
 
24
24
  ## When to use it
25
25
 
26
- - After `dev-local` / `local` (which skip Review + Test) to run the full quality tail on the same branch.
26
+ - After a `:local` run answered Short (which skips Review + Test) to run the full quality tail on the same branch.
27
27
  - After hand-coding / hand-testing a change, to get review + build/test + PR + Jira write-up without re-running dev.
28
28
 
29
29
  ## When NOT to use it