@mmerterden/multi-agent-pipeline 16.27.0 → 16.29.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 +144 -1
- package/README.md +4 -4
- package/README.tr.md +3 -3
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/install/claude.mjs +17 -0
- package/package.json +4 -4
- package/pipeline/commands/multi-agent/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +93 -0
- package/pipeline/commands/multi-agent/doctor/SKILL.md +78 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/jira/SKILL.md +3 -0
- package/pipeline/commands/multi-agent/log/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +14 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +12 -9
- package/pipeline/commands/multi-agent/update/SKILL.md +12 -0
- package/pipeline/lib/_jira-auth.sh +99 -0
- package/pipeline/lib/analysis-jira-write.sh +203 -0
- package/pipeline/lib/issue-fetcher.sh +138 -9
- package/pipeline/lib/multi-repo-pipeline.sh +8 -0
- package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
- package/pipeline/multi-agent-refs/analysis/intake.md +11 -2
- package/pipeline/multi-agent-refs/analysis/locked.md +1 -0
- package/pipeline/multi-agent-refs/analysis/redesign.md +112 -0
- package/pipeline/multi-agent-refs/analysis/render.md +6 -1
- package/pipeline/multi-agent-refs/analysis/resolve.md +1 -0
- package/pipeline/multi-agent-refs/analysis/review.md +15 -0
- package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +3 -3
- package/pipeline/multi-agent-refs/analysis-template.md +36 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +6 -3
- package/pipeline/multi-agent-refs/features/analysis-jira.md +128 -0
- package/pipeline/multi-agent-refs/features/doctor.md +197 -0
- package/pipeline/multi-agent-refs/features/jira-context.md +101 -0
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -7
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
- package/pipeline/multi-agent-refs/picker-contract.md +35 -0
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/tracker-contract.md +5 -1
- package/pipeline/preferences-template.json +1 -1
- package/pipeline/schemas/agent-state.schema.json +24 -0
- package/pipeline/schemas/analysis-spec.schema.json +336 -95
- package/pipeline/schemas/prefs.schema.json +86 -2
- package/pipeline/scripts/analysis-story-tree.mjs +441 -0
- package/pipeline/scripts/anonymize-findings.mjs +24 -0
- package/pipeline/scripts/build-references.mjs +10 -6
- package/pipeline/scripts/council-view.mjs +144 -0
- package/pipeline/scripts/doctor.mjs +758 -0
- package/pipeline/scripts/phase-tracker.sh +100 -3
- package/pipeline/scripts/scan-agent-config.sh +48 -10
- package/pipeline/scripts/skill-siblings.mjs +42 -9
- package/pipeline/scripts/validate-analysis-doc.mjs +371 -7
- package/pipeline/scripts/validate-analysis.mjs +7 -5
- package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +94 -0
- package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +79 -0
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -0
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +13 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +9 -6
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +18 -0
|
@@ -570,7 +570,11 @@ tracker_next_hint() {
|
|
|
570
570
|
[ "${TRACKER_QUIET:-0}" = "1" ] && return 0
|
|
571
571
|
name=$(load_state | jq -r --arg id "$pid" '(.phases[] | select(.id == $id) | .name) // ""')
|
|
572
572
|
case "$(host_kind)" in
|
|
573
|
-
claude) mirror="TaskUpdate(\"
|
|
573
|
+
claude) mirror="TaskUpdate(\"$(subjects "$pid")\", status=\"$status\") - the subject is re-read from \`phase-tracker.sh subjects $pid\` so the widget carries the model, the elapsed time and the tokens; if TaskUpdate is not one of your tools, paste the card above into your reply VERBATIM in a code block (every phase on its own line, keep the elapsed and token columns; do not redraw or compact it)" ;;
|
|
574
|
+
# Not every session carries the task tools: Claude Code provides them by
|
|
575
|
+
# default only up to Opus 4.7 / Sonnet 4.6, a default that landed in
|
|
576
|
+
# v2.1.268. Naming the fallback on the same line is what keeps a newer model
|
|
577
|
+
# from advancing eight phases in silence.
|
|
574
578
|
codex) mirror="update_plan: set step \"Phase $pid $name\" to $status (send the FULL step list, it is not a delta)" ;;
|
|
575
579
|
*) mirror="no task widget on this host - reprint the card above inside your reply text" ;;
|
|
576
580
|
esac
|
|
@@ -661,6 +665,61 @@ EOF
|
|
|
661
665
|
printf '\n'
|
|
662
666
|
}
|
|
663
667
|
|
|
668
|
+
# The native widget renders one row per task and gives us exactly one string to
|
|
669
|
+
# fill it: the subject. So the numbers a reader wants - which model is spending,
|
|
670
|
+
# how long the phase has run, what it cost - have to travel IN the subject or not
|
|
671
|
+
# at all. `render` already computes all of it for the fallback card; this prints
|
|
672
|
+
# the same values in the one shape the host will accept.
|
|
673
|
+
#
|
|
674
|
+
# Every segment is omitted while it is empty, so a pending phase is just its name
|
|
675
|
+
# and a finished one carries its whole bill. Separator is ` - `, not a middle dot:
|
|
676
|
+
# a subject is a task title and travels through surfaces that are not a terminal.
|
|
677
|
+
subjects() {
|
|
678
|
+
need_jq
|
|
679
|
+
local state want="${1:-}"
|
|
680
|
+
state=$(load_state)
|
|
681
|
+
local prices_json='{"prices":{}}'
|
|
682
|
+
if [ -f "$COST_TABLE" ]; then
|
|
683
|
+
prices_json=$(cat "$COST_TABLE" 2>/dev/null) || prices_json='{"prices":{}}'
|
|
684
|
+
echo "$prices_json" | jq empty 2>/dev/null || prices_json='{"prices":{}}'
|
|
685
|
+
fi
|
|
686
|
+
local now_s; now_s=$(now_epoch)
|
|
687
|
+
local rows
|
|
688
|
+
rows=$(echo "$state" | jq -r --argjson prices "$prices_json" "$COST_JQ_DEFS"'
|
|
689
|
+
def usd_of(p): cost_usd_of($prices.prices[p.model // ""] // null; (p.tokens_in // 0); (p.tokens_out // 0); (p.tokens_cached // 0));
|
|
690
|
+
.phases // []
|
|
691
|
+
| sort_by(.id | (tonumber? // 9999))
|
|
692
|
+
| .[] | [
|
|
693
|
+
(.id // ""), (.name // ""), (.status // ""),
|
|
694
|
+
(.started_at // ""), (.completed_at // ""), (.model // ""),
|
|
695
|
+
(((.tokens_in // 0) + (.tokens_out // 0)) | tostring),
|
|
696
|
+
(usd_of(.) | if . == null then "" else (((. * 100) | floor) / 100 | tostring) end)
|
|
697
|
+
] | join("\u001f")')
|
|
698
|
+
local pid pname pstatus p_start p_end pmodel ptok pusd
|
|
699
|
+
while IFS=$'\x1f' read -r pid pname pstatus p_start p_end pmodel ptok pusd; do
|
|
700
|
+
[ -n "$pid" ] || continue
|
|
701
|
+
[ -z "$want" ] || [ "$want" = "$pid" ] || continue
|
|
702
|
+
local line="Phase $pid $pname"
|
|
703
|
+
[ -n "$pmodel" ] && line="$line - $pmodel"
|
|
704
|
+
local s_ep e_ep el=""
|
|
705
|
+
s_ep=$(iso_to_epoch "$p_start")
|
|
706
|
+
e_ep=$(iso_to_epoch "$p_end")
|
|
707
|
+
if [ -n "$s_ep" ]; then
|
|
708
|
+
case "$pstatus" in
|
|
709
|
+
completed|failed|skipped) [ -n "$e_ep" ] && el=$((e_ep - s_ep)) ;;
|
|
710
|
+
in_progress) el=$((now_s - s_ep)) ;;
|
|
711
|
+
esac
|
|
712
|
+
fi
|
|
713
|
+
if [ -n "$el" ] && [ "$el" -lt 0 ] 2>/dev/null; then el=0; fi
|
|
714
|
+
[ -n "$el" ] && line="$line - $(format_elapsed "$el")"
|
|
715
|
+
if [ "${ptok:-0}" -gt 0 ] 2>/dev/null; then
|
|
716
|
+
line="$line - $(format_tokens "$ptok") tok"
|
|
717
|
+
[ -n "$pusd" ] && line="$line - $(printf '~$%.2f' "$pusd" 2>/dev/null || echo '')"
|
|
718
|
+
fi
|
|
719
|
+
printf '%s\n' "$line"
|
|
720
|
+
done <<< "$rows"
|
|
721
|
+
}
|
|
722
|
+
|
|
664
723
|
render() {
|
|
665
724
|
need_jq
|
|
666
725
|
local state
|
|
@@ -1156,6 +1215,10 @@ GATE
|
|
|
1156
1215
|
fi
|
|
1157
1216
|
;;
|
|
1158
1217
|
|
|
1218
|
+
subjects)
|
|
1219
|
+
subjects "${1:-}"
|
|
1220
|
+
;;
|
|
1221
|
+
|
|
1159
1222
|
tiles)
|
|
1160
1223
|
need_jq
|
|
1161
1224
|
tiles_state=$(load_state)
|
|
@@ -1166,10 +1229,44 @@ GATE
|
|
|
1166
1229
|
}
|
|
1167
1230
|
case "$(host_kind)" in
|
|
1168
1231
|
claude)
|
|
1232
|
+
# The task tools are not in every session, and the reason is the model,
|
|
1233
|
+
# not the CLI version. Claude Code provides TaskCreate / TaskUpdate by
|
|
1234
|
+
# default only on Claude 3.x, Opus 4 through 4.7, Sonnet 4 through 4.6
|
|
1235
|
+
# and Haiku 4.5; on any newer model it leaves them out unless the user
|
|
1236
|
+
# opts in, and that default landed in v2.1.268. This contract was written
|
|
1237
|
+
# when the tools were universal and stayed true for years, so nothing
|
|
1238
|
+
# noticed: the tracker state was written correctly, every phase advanced,
|
|
1239
|
+
# and the screen stayed empty for a whole run.
|
|
1240
|
+
#
|
|
1241
|
+
# A shell script cannot probe the model's tool list, so the branch is
|
|
1242
|
+
# taken where the information actually lives - the model knows which
|
|
1243
|
+
# tools it has. And the fallback is not enough on its own: a user looking
|
|
1244
|
+
# at a missing widget needs the one command that brings it back, which is
|
|
1245
|
+
# why the opt-in is printed next to it.
|
|
1169
1246
|
echo "REQUIRED - create one native tile per phase, in this exact order,"
|
|
1170
1247
|
echo "BEFORE any TaskUpdate. The widget renders by creation order, not by"
|
|
1171
1248
|
echo "phase number, so an out-of-order call scrambles the stack."
|
|
1172
|
-
|
|
1249
|
+
subjects | sed 's/^/ TaskCreate(subject: "/; s/$/")/'
|
|
1250
|
+
echo
|
|
1251
|
+
echo "At every phase boundary re-run \`phase-tracker.sh subjects <id>\` and"
|
|
1252
|
+
echo "pass that line as the subject of the TaskUpdate. The subject is the only"
|
|
1253
|
+
echo "string the native widget renders, so the model, the elapsed time and the"
|
|
1254
|
+
echo "token count reach the user through it or not at all."
|
|
1255
|
+
echo
|
|
1256
|
+
echo "IF TaskCreate IS NOT ONE OF YOUR TOOLS this session's model does not"
|
|
1257
|
+
echo "carry them (default since v2.1.268 on models newer than Opus 4.7 /"
|
|
1258
|
+
echo "Sonnet 4.6). Then the card below IS the widget, and at every phase"
|
|
1259
|
+
echo "boundary you run \`phase-tracker.sh render\` and paste its output"
|
|
1260
|
+
echo "into your reply VERBATIM, inside a code block - every phase on"
|
|
1261
|
+
echo "its own line, with the elapsed time, the token count and the total"
|
|
1262
|
+
echo "row exactly as printed. Do NOT redraw it, do not compact phases"
|
|
1263
|
+
echo "onto one line, do not drop the columns: those numbers are the"
|
|
1264
|
+
echo "whole reason the card is worth showing. Tool output is collapsed,"
|
|
1265
|
+
echo "so a card left in stdout never reaches the user."
|
|
1266
|
+
echo "Say once, in outputLanguage, that the native widget returns with:"
|
|
1267
|
+
echo " CLAUDE_CODE_ENABLE_TODO_TOOLS=1 claude"
|
|
1268
|
+
echo
|
|
1269
|
+
render
|
|
1173
1270
|
;;
|
|
1174
1271
|
codex)
|
|
1175
1272
|
echo "REQUIRED - register the plan in ONE update_plan call with this step"
|
|
@@ -1195,7 +1292,7 @@ GATE
|
|
|
1195
1292
|
;;
|
|
1196
1293
|
|
|
1197
1294
|
*)
|
|
1198
|
-
echo "phase-tracker: unknown action '$ACTION' (use init|add|update|sub|tokens|model|meta|now|cost|render)" >&2
|
|
1295
|
+
echo "phase-tracker: unknown action '$ACTION' (use init|add|update|sub|tokens|model|meta|now|cost|render|subjects)" >&2
|
|
1199
1296
|
exit 64
|
|
1200
1297
|
;;
|
|
1201
1298
|
esac
|
|
@@ -18,7 +18,24 @@ set -uo pipefail
|
|
|
18
18
|
|
|
19
19
|
# ROOT defaults to the repo root; SCAN_ROOT overrides it (used by the smoke test
|
|
20
20
|
# to point at a fixture tree of planted-bad configs).
|
|
21
|
-
|
|
21
|
+
#
|
|
22
|
+
# Two layouts, and this script ships into the second one. From the checkout, $0
|
|
23
|
+
# is <repo>/pipeline/scripts/... and `../..` is the repo. From an install it is
|
|
24
|
+
# ~/.claude/scripts/... and `../..` is the HOME directory, where none of the
|
|
25
|
+
# shipped config paths exist - the scan then found zero files and called itself
|
|
26
|
+
# clean. Same defect class as the one skill-siblings.mjs carried, so it gets the
|
|
27
|
+
# same two-candidate resolution: try the repo layout, fall back to the install.
|
|
28
|
+
if [ -n "${SCAN_ROOT:-}" ]; then
|
|
29
|
+
ROOT="$SCAN_ROOT"
|
|
30
|
+
else
|
|
31
|
+
_here="$(cd "$(dirname "$0")" && pwd)"
|
|
32
|
+
_repo="$(cd "$_here/../.." && pwd)"
|
|
33
|
+
if [ -d "$_repo/pipeline/agents" ]; then
|
|
34
|
+
ROOT="$_repo"
|
|
35
|
+
else
|
|
36
|
+
ROOT="$HOME/.claude"
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
22
39
|
|
|
23
40
|
HIGH=0; MED=0
|
|
24
41
|
high() { HIGH=$((HIGH+1)); echo " [HIGH] $1"; }
|
|
@@ -27,18 +44,39 @@ med() { MED=$((MED+1)); echo " [MEDIUM] $1"; }
|
|
|
27
44
|
# Shipped config surface (globs expanded safely; missing paths are skipped).
|
|
28
45
|
TARGETS=()
|
|
29
46
|
add() { [ -e "$1" ] && TARGETS+=("$1"); }
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
# The same surface under either layout: `pipeline/`-prefixed in the checkout,
|
|
48
|
+
# flat under ~/.claude once installed.
|
|
49
|
+
if [ -d "$ROOT/pipeline/agents" ]; then
|
|
50
|
+
add "$ROOT/install/templates/claude-hooks.json"
|
|
51
|
+
add "$ROOT/install/templates/copilot-instructions.md"
|
|
52
|
+
add "$ROOT/pipeline/preferences-template.json"
|
|
53
|
+
for f in "$ROOT"/pipeline/agents/*.md; do add "$f"; done
|
|
54
|
+
# (figma component skills + their scripts now live in the ai-*-toolkit
|
|
55
|
+
# marketplace plugin, not in this repo, so there is nothing figma to scan here.)
|
|
56
|
+
add "$ROOT/pipeline/scripts/agent-guard.sh"
|
|
57
|
+
add "$ROOT/pipeline/scripts/pre-commit-check.sh"
|
|
58
|
+
else
|
|
59
|
+
# settings.json is deliberately NOT in this list. It is the USER's file, not
|
|
60
|
+
# something the pipeline ships, and their own permission choices are theirs to
|
|
61
|
+
# make - scanning it turned a personal setting into a release blocker.
|
|
62
|
+
for f in "$ROOT"/templates/*; do add "$f"; done
|
|
63
|
+
for f in "$ROOT"/agents/*.md; do add "$f"; done
|
|
64
|
+
add "$ROOT/scripts/agent-guard.sh"
|
|
65
|
+
add "$ROOT/scripts/pre-commit-check.sh"
|
|
66
|
+
fi
|
|
38
67
|
|
|
39
68
|
rel() { echo "${1#$ROOT/}"; }
|
|
40
69
|
|
|
41
|
-
echo "→ scanning ${#TARGETS[@]} shipped config files"
|
|
70
|
+
echo "→ scanning ${#TARGETS[@]} shipped config files (root: $ROOT)"
|
|
71
|
+
|
|
72
|
+
# Zero targets is a resolution failure, not a clean bill: `"${TARGETS[@]}"` on an
|
|
73
|
+
# empty array is also an unbound-variable error under bash 3.2 with `set -u`, so
|
|
74
|
+
# the script died here instead of saying what went wrong.
|
|
75
|
+
if [ "${#TARGETS[@]}" -eq 0 ]; then
|
|
76
|
+
echo " [HIGH] no shipped config file found under $ROOT - nothing was scanned"
|
|
77
|
+
echo "→ config hygiene: 1 HIGH, 0 MEDIUM"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
42
80
|
|
|
43
81
|
for f in "${TARGETS[@]}"; do
|
|
44
82
|
[ -f "$f" ] || continue
|
|
@@ -44,14 +44,23 @@ import { homedir } from "node:os";
|
|
|
44
44
|
import { fileURLToPath } from "node:url";
|
|
45
45
|
|
|
46
46
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
47
|
-
const ROOT = resolve(HERE, "..", "..");
|
|
48
47
|
const HOME = homedir();
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
// Two layouts, and the script ships into the second one. From the repo checkout
|
|
50
|
+
// HERE is <repo>/pipeline/scripts, so the authored trees sit one level up. From
|
|
51
|
+
// an install HERE is ~/.claude/scripts and there is no `pipeline/` above it:
|
|
52
|
+
// resolving blind reported "no commands found" and made every caller believe
|
|
53
|
+
// the tree had changed. Try the repo first, fall back to the install.
|
|
54
|
+
const REPO_ROOT = resolve(HERE, "..", "..");
|
|
55
|
+
const REPO_CMD_DIR = join(REPO_ROOT, "pipeline", "commands", "multi-agent");
|
|
56
|
+
const IN_REPO = existsSync(REPO_CMD_DIR);
|
|
57
|
+
const ROOT = IN_REPO ? REPO_ROOT : HOME;
|
|
58
|
+
|
|
59
|
+
const CMD_DIR = IN_REPO ? REPO_CMD_DIR : join(HOME, ".claude", "commands", "multi-agent");
|
|
60
|
+
const CORE_DIR = join(REPO_ROOT, "pipeline", "skills", "shared", "core");
|
|
52
61
|
const COPILOT_DIR = join(HOME, ".copilot", "skills");
|
|
53
62
|
const CODEX_DIR = join(HOME, ".codex", "multi-agent-refs", "commands");
|
|
54
|
-
// The dispatcher is the one skill Codex installs as a SKILL: the
|
|
63
|
+
// The dispatcher is the one skill Codex installs as a SKILL: the sub-commands
|
|
55
64
|
// become refs there, because Codex truncates a large skills block. Resolving it
|
|
56
65
|
// under the refs tree like the others reported a false "absent" - and a sibling
|
|
57
66
|
// check that invents a missing copy is worse than none, since it sends the next
|
|
@@ -107,11 +116,23 @@ function siblingsOf(name) {
|
|
|
107
116
|
);
|
|
108
117
|
const installedName = name === "multi-agent" ? "multi-agent" : `multi-agent-${name}`;
|
|
109
118
|
|
|
119
|
+
// The two authored surfaces exist only in the checkout. From an install they
|
|
120
|
+
// are not missing, they are not applicable, and counting them as absent would
|
|
121
|
+
// report every command as 3/5 forever - a number that reads as damage and is
|
|
122
|
+
// really just the layout. In an install CMD_DIR is also the claude tree, so
|
|
123
|
+
// listing "authored" there would double-count one file as two surfaces.
|
|
124
|
+
const authoredSurfaces = IN_REPO
|
|
125
|
+
? [
|
|
126
|
+
{ tree: "command (authored)", path: authored, exists: existsSync(authored), inRepo: true },
|
|
127
|
+
{ tree: "shared core skill", path: shared, exists: existsSync(shared), inRepo: true },
|
|
128
|
+
]
|
|
129
|
+
: [];
|
|
130
|
+
|
|
110
131
|
return {
|
|
111
132
|
name,
|
|
133
|
+
layout: IN_REPO ? "repo" : "install",
|
|
112
134
|
surfaces: [
|
|
113
|
-
|
|
114
|
-
{ tree: "shared core skill", path: shared, exists: existsSync(shared), inRepo: true },
|
|
135
|
+
...authoredSurfaces,
|
|
115
136
|
{
|
|
116
137
|
tree: "claude (installed)",
|
|
117
138
|
path: join(CLAUDE_DIR, name === "multi-agent" ? "SKILL.md" : join(name, "SKILL.md")),
|
|
@@ -145,11 +166,23 @@ if (AUDIT) {
|
|
|
145
166
|
});
|
|
146
167
|
const broken = rows.filter((r) => r.missingRepo.length > 0);
|
|
147
168
|
if (JSON_OUT) {
|
|
148
|
-
process.stdout.write(
|
|
169
|
+
process.stdout.write(
|
|
170
|
+
JSON.stringify(
|
|
171
|
+
{ layout: IN_REPO ? "repo" : "install", commands: names.length, broken, rows },
|
|
172
|
+
null,
|
|
173
|
+
2,
|
|
174
|
+
) + "\n",
|
|
175
|
+
);
|
|
149
176
|
} else {
|
|
150
|
-
process.stdout.write(`${names.length} command(s) checked\n`);
|
|
177
|
+
process.stdout.write(`${names.length} command(s) checked (${IN_REPO ? "repo" : "install"})\n`);
|
|
151
178
|
if (broken.length === 0) {
|
|
152
|
-
|
|
179
|
+
// From an install there is no authored side to check, and claiming one
|
|
180
|
+
// was checked is the failure this script exists to prevent one level up.
|
|
181
|
+
process.stdout.write(
|
|
182
|
+
IN_REPO
|
|
183
|
+
? " every command has its authored sibling in the repo\n"
|
|
184
|
+
: " authored siblings not checked - no repo checkout resolved from here\n",
|
|
185
|
+
);
|
|
153
186
|
}
|
|
154
187
|
for (const r of broken) {
|
|
155
188
|
process.stdout.write(` MISSING ${r.name}: ${r.missingRepo.join(", ")}\n`);
|