@oneie/claude 0.8.0 → 0.10.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/.claude-plugin/plugin.json +1 -1
- package/agents/abm-strategist.md +67 -1
- package/agents/ads-meta.md +67 -1
- package/agents/analyst.md +67 -1
- package/agents/animator.md +108 -0
- package/agents/architect.md +269 -20
- package/agents/brand-guardian.md +67 -1
- package/agents/brand-strategist.md +67 -1
- package/agents/campaign-content.md +67 -1
- package/agents/campaign-email.md +67 -1
- package/agents/campaign-sms.md +67 -1
- package/agents/campaign-social.md +67 -1
- package/agents/cco.md +83 -2
- package/agents/ceo.md +108 -11
- package/agents/chairman.md +197 -0
- package/agents/cmo.md +82 -2
- package/agents/community-greeter.md +67 -1
- package/agents/community-moderator.md +67 -1
- package/agents/compliance.md +67 -1
- package/agents/copywriter.md +67 -1
- package/agents/creative-strategist.md +67 -1
- package/agents/cro.md +81 -1
- package/agents/cto.md +266 -28
- package/agents/customer-interviewer.md +67 -1
- package/agents/customer-researcher.md +67 -1
- package/agents/customer-success-manager.md +67 -1
- package/agents/customer-trainer.md +67 -1
- package/agents/cxo.md +82 -1
- package/agents/demand-creator.md +67 -1
- package/agents/demo-mover.md +67 -1
- package/agents/demo-specialist.md +67 -1
- package/agents/demo-thai-family-law.md +67 -1
- package/agents/designer.md +67 -1
- package/agents/discovery-caller.md +67 -1
- package/agents/doctor.md +269 -0
- package/agents/educate-coach.md +67 -1
- package/agents/elevate-tutor.md +67 -1
- package/agents/email-lifecycle-marketer.md +67 -1
- package/agents/engage-specialist.md +67 -1
- package/agents/events-coordinator.md +67 -1
- package/agents/foundation-builder.md +67 -1
- package/agents/funnel-architect.md +67 -1
- package/agents/gift-creator.md +67 -1
- package/agents/google-ads.md +67 -1
- package/agents/guide.md +67 -1
- package/agents/helpdesk-dispatcher.md +67 -1
- package/agents/hook-specialist.md +67 -1
- package/agents/identify-optimizer.md +67 -1
- package/agents/implementer.md +313 -45
- package/agents/incident-commander.md +67 -1
- package/agents/insights-lead.md +87 -1
- package/agents/journey-runner.md +67 -1
- package/agents/linkedin-ads.md +67 -1
- package/agents/live-sales-chat.md +67 -1
- package/agents/market-researcher.md +67 -1
- package/agents/media-buyer.md +67 -1
- package/agents/memory-keeper.md +195 -0
- package/agents/movers-customer-researcher.md +67 -1
- package/agents/movers-foundation-builder.md +67 -1
- package/agents/movers-market-researcher.md +67 -1
- package/agents/movers-pricing-strategist.md +67 -1
- package/agents/nurture-architect.md +67 -1
- package/agents/offer-architect.md +67 -1
- package/agents/onboarder.md +67 -1
- package/agents/onboarding-specialist.md +67 -1
- package/agents/operations-dashboard.md +87 -1
- package/agents/perf-engineer.md +333 -37
- package/agents/playbook-writer.md +67 -1
- package/agents/plg-strategist.md +67 -1
- package/agents/positioning-architect.md +67 -1
- package/agents/press-officer.md +67 -1
- package/agents/pricing-strategist.md +67 -1
- package/agents/privacy-officer.md +67 -1
- package/agents/referral-manager.md +67 -1
- package/agents/refine-analyst.md +67 -1
- package/agents/release-manager.md +446 -39
- package/agents/renewals-upsell-rep.md +67 -1
- package/agents/review-engineer.md +319 -45
- package/agents/rewards-steward.md +67 -1
- package/agents/sales-call-coach.md +67 -1
- package/agents/sales-closer.md +67 -1
- package/agents/security-auditor.md +343 -48
- package/agents/sell-closer.md +67 -1
- package/agents/share-amplifier.md +67 -1
- package/agents/social-media-manager.md +67 -1
- package/agents/storyteller.md +301 -0
- package/agents/strategist.md +67 -1
- package/agents/strategy-aligner.md +67 -1
- package/agents/support-agent.md +67 -1
- package/agents/tagger.md +327 -0
- package/agents/tech-writer.md +195 -22
- package/agents/test-engineer.md +398 -29
- package/agents/tiktok-ads.md +67 -1
- package/agents/tracking-engineer.md +67 -1
- package/agents/trailkeeper.md +181 -0
- package/agents/upsell-strategist.md +67 -1
- package/agents/voice.md +67 -1
- package/agents/w1-recon.md +1 -1
- package/agents/w2-decide.md +1 -1
- package/agents/w3-edit.md +8 -2
- package/agents/w4-verify.md +13 -0
- package/agents/workflow-optimiser.md +81 -1
- package/commands/close.md +916 -160
- package/commands/deploy.md +102 -724
- package/commands/do.md +58 -2
- package/commands/sweep.md +159 -0
- package/commands/tasks.md +222 -0
- package/hooks/scripts/dev-only.sh +135 -0
- package/hooks/scripts/git-add-guard.sh +37 -2
- package/hooks/scripts/session-start.sh +32 -4
- package/package.json +1 -1
- package/rules/scripts.md +85 -0
- package/scripts/CLAUDE.md +315 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agent-actor-parity.sh +129 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/chrome.mjs +18 -0
- package/scripts/close-metrics.sh +587 -0
- package/scripts/close-owner.sh +326 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-emit.sh +311 -0
- package/scripts/deploy-gate-check.sh +155 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy-schema-check.sh +58 -0
- package/scripts/deploy.sh +393 -243
- package/scripts/do-auto.sh +127 -26
- package/scripts/do-board.sh +429 -0
- package/scripts/do-close.sh +1184 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-fleet.sh +8 -2
- package/scripts/do-plan-json.mjs +110 -12
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +86 -10
- package/scripts/do-rank.py +200 -3
- package/scripts/do-reconcile.sh +73 -12
- package/scripts/do-signal.sh +101 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-w4-gates.sh +11 -1
- package/scripts/do-world-check.sh +153 -0
- package/scripts/download-stats.sh +172 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +18 -1
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +71 -0
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/flywheel-outcome.sh +63 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +9 -0
- package/scripts/gate-watchdog.sh +619 -0
- package/scripts/gc-content-check.sh +142 -0
- package/scripts/gh-traffic-capture.sh +153 -0
- package/scripts/govern-order-check.sh +202 -0
- package/scripts/governor-doors-check.sh +86 -5
- package/scripts/health.sh +448 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/incident.sh +212 -0
- package/scripts/land.sh +755 -45
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/livekit-ratchet.sh +18 -1
- package/scripts/machine-check.sh +1 -1
- package/scripts/memory-index-budget.sh +79 -0
- package/scripts/npm-downloads.sh +109 -0
- package/scripts/one-agents.mjs +204 -8
- package/scripts/one-resume.sh +31 -3
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/redirect-lint.sh +169 -0
- package/scripts/release.sh +40 -6
- package/scripts/resume-lost-sessions.sh +68 -0
- package/scripts/shoot-pages.mjs +140 -0
- package/scripts/signal-meta-backfill.ts +451 -0
- package/scripts/signal-watch.sh +63 -6
- package/scripts/speed-cache-check.sh +12 -2
- package/scripts/sweep.sh +426 -0
- package/scripts/task-titles-dump.ts +101 -0
- package/scripts/test-cached.sh +47 -10
- package/scripts/test-lanes.sh +14 -0
- package/scripts/thread-name-backfill.ts +215 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +155 -8
- package/scripts/typedb-flake-check.sh +3 -1
- package/scripts/urls-lint.sh +8 -0
- package/scripts/verify-board-doors.sh +80 -0
- package/scripts/verify-fast.sh +159 -6
- package/scripts/worktree-up.sh +21 -3
- package/skills/astro/SKILL.md +9 -3
- package/skills/astro/optimize-performance.md +3 -2
- package/skills/cloudflare/SKILL.md +3 -2
- package/skills/cloudflare-security-audit/AI-AND-LLM.md +83 -0
- package/skills/cloudflare-security-audit/ATTACK-CLASSES.md +130 -0
- package/skills/cloudflare-security-audit/CLIENT-SIDE.md +83 -0
- package/skills/cloudflare-security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- package/skills/cloudflare-security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- package/skills/cloudflare-security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- package/skills/cloudflare-security-audit/HUNTING.md +251 -0
- package/skills/cloudflare-security-audit/LICENSE +21 -0
- package/skills/cloudflare-security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- package/skills/cloudflare-security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- package/skills/cloudflare-security-audit/PROVENANCE.md +78 -0
- package/skills/cloudflare-security-audit/RECONNAISSANCE.md +156 -0
- package/skills/cloudflare-security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- package/skills/cloudflare-security-audit/SKILL.md +192 -0
- package/skills/cloudflare-security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- package/skills/cloudflare-security-audit/VALIDATION-AND-REPORTING.md +186 -0
- package/skills/cloudflare-security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- package/skills/cloudflare-security-audit/report-schema.json +461 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.cjs +872 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.test.cjs +740 -0
- package/skills/cloudflare-security-audit/validate-findings.cjs +773 -0
- package/skills/cloudflare-security-audit/validate-findings.test.cjs +652 -0
- package/skills/deploy/REFERENCE.md +713 -0
- package/skills/deploy/SKILL.md +140 -0
- package/skills/fleet-audit/SKILL.md +58 -0
- package/skills/meeting/SKILL.md +220 -0
- package/skills/planning/SKILL.md +256 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/typedb/SKILL.md +7 -0
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/templates/template-agent.md +7 -1
- package/templates/template-feature.md +9 -0
- package/templates/template-todo.md +29 -0
package/scripts/one-resume.sh
CHANGED
|
@@ -51,22 +51,50 @@ done < <(find "$PROJ" -path '*/subagents/workflows/wf_*/journal.jsonl' -mtime -3
|
|
|
51
51
|
|
|
52
52
|
echo
|
|
53
53
|
echo "${DIM}══ branches ══${OFF}"
|
|
54
|
+
# WHY the yardstick is `dev` and not `main`:
|
|
55
|
+
# land.sh merges a branch into TRUNK, and TRUNK is `dev` (land.sh:117 —
|
|
56
|
+
# "TRUNK is what a branch LANDS INTO; PR_BASE is what dev is RELEASED into").
|
|
57
|
+
# Measuring "ready to land" against main therefore counts every commit that
|
|
58
|
+
# is ALREADY INTEGRATED on dev and merely waiting on the dev->main PR, and
|
|
59
|
+
# reports it as unlanded work. Measured 2026-09-15: feat/dashboard,
|
|
60
|
+
# feat/fix-og-screenshot-fullpage and feat/tasks-bulk each read
|
|
61
|
+
# "38 / 20 / 11 commit(s) ready to land" while all three were 0 ahead of
|
|
62
|
+
# origin/dev and already merged — and `--deploy` would have re-run land.sh
|
|
63
|
+
# on all three. This is the same stale-yardstick class as do-auto.sh --gc,
|
|
64
|
+
# but the opposite direction: --gc measures against main and KEEPS too much
|
|
65
|
+
# (safe); this INVENTED work (unsafe), and re-launching landed work is the
|
|
66
|
+
# most expensive mistake the board offers.
|
|
67
|
+
# Remote refs first: a local `main`/`dev` can sit hundreds of commits behind
|
|
68
|
+
# origin while `git status` reports the tree clean.
|
|
69
|
+
_ref() { git rev-parse --verify -q "origin/$1" >/dev/null 2>&1 && echo "origin/$1" || echo "$1"; }
|
|
70
|
+
TRUNK_REF="$(_ref "${LAND_TRUNK:-dev}")" # what a branch lands INTO
|
|
71
|
+
BASE_REF="$(_ref main)" # what dev is RELEASED into
|
|
54
72
|
READY=()
|
|
55
73
|
while IFS= read -r br; do
|
|
56
74
|
[[ -z "$br" ]] && continue
|
|
57
|
-
ahead="$(git rev-list --count "
|
|
75
|
+
ahead="$(git rev-list --count "$TRUNK_REF..$br" 2>/dev/null || echo 0)"
|
|
58
76
|
if [[ "$ahead" == "0" ]]; then
|
|
59
|
-
printf ' %-34s %sno commits beyond
|
|
77
|
+
printf ' %-34s %sno commits beyond %s%s\n' "$br" "$DIM" "$TRUNK_REF" "$OFF"
|
|
60
78
|
else
|
|
61
|
-
printf ' %-34s %s%s commit(s) ready to land%s\n'
|
|
79
|
+
printf ' %-34s %s%s commit(s) ready to land%s %s(-> %s)%s\n' \
|
|
80
|
+
"$br" "$GRN" "$ahead" "$OFF" "$DIM" "$TRUNK_REF" "$OFF"
|
|
62
81
|
READY+=("$br")
|
|
63
82
|
fi
|
|
64
83
|
done < <(git for-each-ref --format='%(refname:short)' --sort=-committerdate refs/heads/feat 2>/dev/null | head -20)
|
|
65
84
|
|
|
85
|
+
# The other half of the loop: what is integrated but not yet released.
|
|
86
|
+
rel="$(git rev-list --count "$BASE_REF..$TRUNK_REF" 2>/dev/null || echo 0)"
|
|
87
|
+
if [[ "$rel" != "0" ]]; then
|
|
88
|
+
printf '\n %-34s %s%s commit(s) ready to release%s %s(%s -> %s, by PR)%s\n' \
|
|
89
|
+
"${LAND_TRUNK:-dev}" "$YEL" "$rel" "$OFF" "$DIM" "$TRUNK_REF" "$BASE_REF" "$OFF"
|
|
90
|
+
fi
|
|
91
|
+
|
|
66
92
|
echo
|
|
67
93
|
echo "${DIM}══ next ══${OFF}"
|
|
68
94
|
if (( ${#READY[@]} )); then
|
|
69
95
|
echo " bash .claude/scripts/land.sh ${READY[*]} --deploy"
|
|
96
|
+
elif [[ "$rel" != "0" ]]; then
|
|
97
|
+
echo " gh pr create --base main --head ${LAND_TRUNK:-dev} # $rel commit(s) integrated, not released"
|
|
70
98
|
else
|
|
71
99
|
echo " nothing to land yet — resume a fleet above, or ./deploy dev"
|
|
72
100
|
fi
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pr-body.sh — the pull request a finished branch deserves, derived, never written.
|
|
3
|
+
#
|
|
4
|
+
# bash .claude/scripts/pr-body.sh feat/editor-panel-tabs
|
|
5
|
+
# bash .claude/scripts/pr-body.sh do/my-plan --base main --gate "fast lane (green)"
|
|
6
|
+
# bash .claude/scripts/pr-body.sh feat/x --dev "https://dev.one.ie — 2 routes proven"
|
|
7
|
+
# bash .claude/scripts/pr-body.sh feat/x --title # just the title line
|
|
8
|
+
# bash .claude/scripts/pr-body.sh --self-test # the red proofs
|
|
9
|
+
#
|
|
10
|
+
# WHY THIS EXISTS: a /do branch arrives at the merge door carrying no explanation.
|
|
11
|
+
# Context isolation throws away each cycle's reasoning to save 90-150k tokens, so
|
|
12
|
+
# the prose that would have justified the diff is gone by the time a human reads
|
|
13
|
+
# it. `do-auto.sh`'s `_merge_digest` solved that for the LOCAL reader by writing
|
|
14
|
+
# `.do-digest.md` at trunk root. A pull request is the same document with an
|
|
15
|
+
# audience: same facts, same sources, addressed to a reviewer instead of to the
|
|
16
|
+
# operator standing in the tree.
|
|
17
|
+
#
|
|
18
|
+
# EVERY FIELD IS DERIVED FROM GIT AT CALL TIME. It deliberately does NOT read
|
|
19
|
+
# `.do-digest.md`, even when one is sitting there: that file lives at trunk root,
|
|
20
|
+
# is uncommitted, is written per-slug and is overwritten by the next cycle, so a
|
|
21
|
+
# body that were "the digest when present, git otherwise" would be right
|
|
22
|
+
# sometimes and stale sometimes with nothing on its face to say which. Deriving
|
|
23
|
+
# every time is slower by milliseconds and always true.
|
|
24
|
+
#
|
|
25
|
+
# THE DIFF IS `merge-base..BR`, NEVER `BASE..BR`. Two-dot from the base conflates
|
|
26
|
+
# what this branch added with what trunk advanced while it was building — the
|
|
27
|
+
# latter reads as spurious reversions and inflates every count in the body. Same
|
|
28
|
+
# rule `_merge_digest` follows, and the same reason.
|
|
29
|
+
#
|
|
30
|
+
# A MEASUREMENT THAT IS NOT THERE IS NOT RENDERED. The trust score, the open
|
|
31
|
+
# improvements and the cycle count come from gitignored artifacts that exist only
|
|
32
|
+
# beside a live worktree; the plan title comes from a file that only a /do branch
|
|
33
|
+
# has. When one is absent its line is omitted — never printed as 0, "?" or
|
|
34
|
+
# "unknown", which read as measured facts.
|
|
35
|
+
#
|
|
36
|
+
# Exit codes: 0 emitted · 2 bad usage · 3 no such branch · 4 self-test failed.
|
|
37
|
+
set -uo pipefail
|
|
38
|
+
# The repo this reads. Overridable ONLY through a dedicated name so the
|
|
39
|
+
# self-test can point it at a scratch repo; a generic `ROOT` would inherit
|
|
40
|
+
# whatever a calling script happened to export and read the wrong tree.
|
|
41
|
+
ROOT="${PR_BODY_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
|
42
|
+
|
|
43
|
+
BR="" BASE="main" GATE="" DEV="" TITLE_ONLY=0 SELF_TEST=0
|
|
44
|
+
while [ $# -gt 0 ]; do
|
|
45
|
+
case "$1" in
|
|
46
|
+
--base) BASE="${2:-main}"; shift 2 ;;
|
|
47
|
+
--gate) GATE="${2:-}"; shift 2 ;;
|
|
48
|
+
--dev) DEV="${2:-}"; shift 2 ;;
|
|
49
|
+
--title) TITLE_ONLY=1; shift ;;
|
|
50
|
+
--self-test) SELF_TEST=1; shift ;;
|
|
51
|
+
-h|--help) sed -n '2,10p' "${BASH_SOURCE[0]}"; exit 0 ;;
|
|
52
|
+
-*) echo "pr-body.sh: unknown flag '$1'" >&2; exit 2 ;;
|
|
53
|
+
*) BR="$1"; shift ;;
|
|
54
|
+
esac
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# --- the plan behind a branch ------------------------------------------------
|
|
58
|
+
# `feat/editor-panel-tabs` and `do/editor-panel-tabs` are both the slug
|
|
59
|
+
# `editor-panel-tabs`; a bare branch name is its own slug.
|
|
60
|
+
slug_of() { printf '%s' "${1#*/}"; }
|
|
61
|
+
|
|
62
|
+
# The worktree holding a branch, if any. The gitignored build artifacts
|
|
63
|
+
# (.do-trust.json, .w4-improvements.json) live there and nowhere else, so a
|
|
64
|
+
# branch with no worktree simply reports fewer facts.
|
|
65
|
+
worktree_of() {
|
|
66
|
+
git worktree list --porcelain \
|
|
67
|
+
| awk -v b="refs/heads/$1" '/^worktree /{w=$2} /^branch /{if ($2==b) print w}' \
|
|
68
|
+
| head -1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# THE PLAN IS READ FROM THE BRANCH, NOT FROM THE TREE THIS RUNS IN.
|
|
72
|
+
#
|
|
73
|
+
# A /do branch commits its own `text/<slug>-todo.md`, and that copy is the one
|
|
74
|
+
# whose cycles this branch actually closed. The checked-out tree may not have the
|
|
75
|
+
# file at all (a plan authored entirely on the branch) or may have a STALE copy —
|
|
76
|
+
# `do-auto.sh` blind-syncs `<slug>-plan.md` from main every iteration, so a tree
|
|
77
|
+
# copy is not evidence about a branch. Caught by the throwaway proof: reading
|
|
78
|
+
# $ROOT silently dropped the title, the cycle counts and the kill-switch, and the
|
|
79
|
+
# body still looked complete because every one of those lines is optional.
|
|
80
|
+
#
|
|
81
|
+
# Falls back to the working tree so a branch with no committed plan (a hand-made
|
|
82
|
+
# `feat/*`) can still pick up a plan that exists only locally.
|
|
83
|
+
todo_for() {
|
|
84
|
+
local br="$1" slug="$2" out
|
|
85
|
+
out="$(mktemp)"
|
|
86
|
+
if git -C "$ROOT" show "${br}:text/${slug}-todo.md" > "$out" 2>/dev/null && [ -s "$out" ]; then
|
|
87
|
+
printf '%s' "$out"; return 0
|
|
88
|
+
fi
|
|
89
|
+
rm -f "$out"
|
|
90
|
+
[ -f "$ROOT/text/${slug}-todo.md" ] && { printf '%s' "$ROOT/text/${slug}-todo.md"; return 0; }
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# `title: Some Words` out of a todo's frontmatter. Frontmatter only — a `title:`
|
|
95
|
+
# in the body of a 900-line plan is somebody's YAML example, not the plan's name.
|
|
96
|
+
plan_title() {
|
|
97
|
+
local f="$1"
|
|
98
|
+
[ -f "$f" ] || return 1
|
|
99
|
+
awk '
|
|
100
|
+
NR==1 && $0=="---" { inside=1; next }
|
|
101
|
+
inside && $0=="---" { exit }
|
|
102
|
+
inside && /^title:[[:space:]]/ { sub(/^title:[[:space:]]*/, ""); print; exit }
|
|
103
|
+
' "$f"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
emit() {
|
|
107
|
+
local br="$1" base="$2"
|
|
108
|
+
local mb behind shortstat slug todo wt title
|
|
109
|
+
slug="$(slug_of "$br")"
|
|
110
|
+
todo="$(todo_for "$br" "$slug" || true)"
|
|
111
|
+
wt="$(worktree_of "$br")"
|
|
112
|
+
|
|
113
|
+
mb="$(git -C "$ROOT" merge-base "$base" "$br" 2>/dev/null || printf '%s' "$base")"
|
|
114
|
+
behind="$(git -C "$ROOT" rev-list --count "$mb..$base" 2>/dev/null || echo 0)"
|
|
115
|
+
shortstat="$(git -C "$ROOT" diff --shortstat "$mb..$br" 2>/dev/null || true)"
|
|
116
|
+
|
|
117
|
+
title="$(plan_title "$todo" 2>/dev/null || true)"
|
|
118
|
+
# No plan, or a plan with no title: the branch's own first commit says what it
|
|
119
|
+
# set out to do, which is the next most honest sentence available.
|
|
120
|
+
[ -n "$title" ] || title="$(git -C "$ROOT" log --format=%s "$mb..$br" 2>/dev/null | tail -1)"
|
|
121
|
+
[ -n "$title" ] || title="$br"
|
|
122
|
+
|
|
123
|
+
if [ "$TITLE_ONLY" = "1" ]; then printf '%s\n' "$title"; return 0; fi
|
|
124
|
+
|
|
125
|
+
echo "## What this is"
|
|
126
|
+
echo
|
|
127
|
+
echo "Branch \`${br}\` → \`${base}\`. Generated by \`.claude/scripts/pr-body.sh\` from git —"
|
|
128
|
+
echo "every number below is derived at PR time, not written by hand or by a model."
|
|
129
|
+
echo
|
|
130
|
+
[ -n "$todo" ] && [ -f "$todo" ] && echo "Plan: \`text/${slug}-todo.md\`"
|
|
131
|
+
[ -n "$todo" ] && [ -f "$todo" ] && echo
|
|
132
|
+
|
|
133
|
+
# --- the counts ------------------------------------------------------------
|
|
134
|
+
echo "## Receipts"
|
|
135
|
+
echo
|
|
136
|
+
if [ -n "$todo" ] && [ -f "$todo" ]; then
|
|
137
|
+
local closed total
|
|
138
|
+
closed="$(grep -cE '\[[xX]\] \*{0,2}C[0-9]+' "$todo" 2>/dev/null || true)"
|
|
139
|
+
total="$(grep -cE '\[[ xX]\] \*{0,2}C[0-9]+' "$todo" 2>/dev/null || true)"
|
|
140
|
+
[ "${closed:-0}" != "0" ] || [ "${total:-0}" != "0" ] \
|
|
141
|
+
&& echo "- **Cycles closed:** ${closed:-0} of ${total:-0}"
|
|
142
|
+
fi
|
|
143
|
+
echo "- **This branch's changes (merge-base..${br}):** ${shortstat:-none}"
|
|
144
|
+
[ "${behind:-0}" -gt 0 ] \
|
|
145
|
+
&& echo "- **Trunk advanced during build:** ${behind} commit(s) — the diff above excludes them"
|
|
146
|
+
|
|
147
|
+
# Mergeability WITHOUT mutating anything. `--write-tree` computes the merge in
|
|
148
|
+
# the object store and says whether it conflicts; it does not touch a worktree,
|
|
149
|
+
# an index or a ref, which is why this is safe to run against a shared trunk.
|
|
150
|
+
if git -C "$ROOT" merge-tree --write-tree "$base" "$br" >/dev/null 2>&1; then
|
|
151
|
+
echo "- **Merges cleanly into \`${base}\`:** yes"
|
|
152
|
+
else
|
|
153
|
+
echo "- **Merges cleanly into \`${base}\`:** NO — conflicts, resolve before merging"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Gate result is PASSED IN by the caller that actually ran one. This script
|
|
157
|
+
# runs no gate, so it must never imply it did: no --gate, no line.
|
|
158
|
+
[ -n "$GATE" ] && echo "- **Gate:** ${GATE}"
|
|
159
|
+
|
|
160
|
+
# Same rule, one door further along: whether this branch was ever RUN, and
|
|
161
|
+
# where. `--dev` is passed only by a caller that actually shipped the branch
|
|
162
|
+
# to dev.one.ie and probed it. No flag, no line — a PR that says nothing
|
|
163
|
+
# about dev is a PR whose branch was never run, and that is the honest read.
|
|
164
|
+
[ -n "$DEV" ] && echo "- **Ran on dev:** ${DEV}"
|
|
165
|
+
|
|
166
|
+
if [ -n "$wt" ] && [ -f "$wt/.do-trust.json" ]; then
|
|
167
|
+
local trust composite
|
|
168
|
+
trust="$(jq -r '.level // empty' "$wt/.do-trust.json" 2>/dev/null || true)"
|
|
169
|
+
composite="$(jq -r '.composite // empty' "$wt/.do-trust.json" 2>/dev/null || true)"
|
|
170
|
+
[ -n "$trust$composite" ] \
|
|
171
|
+
&& echo "- **Rubric:** trust=${trust:-—} · composite=${composite:-—}"
|
|
172
|
+
fi
|
|
173
|
+
if [ -n "$wt" ] && [ -f "$wt/.w4-improvements.json" ]; then
|
|
174
|
+
local open
|
|
175
|
+
open="$(jq -r 'if type=="array" then length elif type=="object" then ([.[]?]|length) else 0 end' \
|
|
176
|
+
"$wt/.w4-improvements.json" 2>/dev/null || true)"
|
|
177
|
+
[ -n "$open" ] && echo "- **Open improvements carried:** ${open}"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# The kill-switch, quoted so a reviewer can run the same command the plan
|
|
181
|
+
# gates on rather than taking "it works" on trust.
|
|
182
|
+
if [ -n "$todo" ] && [ -f "$todo" ]; then
|
|
183
|
+
local outcome
|
|
184
|
+
outcome="$(awk '
|
|
185
|
+
NR==1 && $0=="---" { inside=1; next }
|
|
186
|
+
inside && $0=="---" { exit }
|
|
187
|
+
inside && /^outcome:[[:space:]]/ { sub(/^outcome:[[:space:]]*/, ""); print; exit }
|
|
188
|
+
' "$todo" 2>/dev/null | sed 's/^"//; s/"$//')"
|
|
189
|
+
if [ -n "$outcome" ]; then
|
|
190
|
+
echo
|
|
191
|
+
echo "**Outcome check** — the plan's own kill-switch, exits 0 when the goal is met:"
|
|
192
|
+
echo
|
|
193
|
+
echo '```bash'
|
|
194
|
+
echo "$outcome"
|
|
195
|
+
echo '```'
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
echo
|
|
200
|
+
echo "## Files changed (this branch only)"
|
|
201
|
+
echo
|
|
202
|
+
echo '```'
|
|
203
|
+
git -C "$ROOT" diff --stat "$mb..$br" 2>/dev/null || true
|
|
204
|
+
echo '```'
|
|
205
|
+
echo
|
|
206
|
+
echo "## Commits"
|
|
207
|
+
echo
|
|
208
|
+
echo '```'
|
|
209
|
+
git -C "$ROOT" log --oneline "$mb..$br" 2>/dev/null || true
|
|
210
|
+
echo '```'
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# --- self-test ---------------------------------------------------------------
|
|
214
|
+
# The point is that each check can go RED. A body generator that emits a
|
|
215
|
+
# plausible paragraph no matter what it was handed proves nothing, and every
|
|
216
|
+
# claim below is one this script got wrong at least once while being written.
|
|
217
|
+
self_test() {
|
|
218
|
+
local tmp fails=0
|
|
219
|
+
tmp="$(mktemp -d)"
|
|
220
|
+
trap 'rm -rf "$tmp"' RETURN
|
|
221
|
+
t_ok() { printf ' ✓ %s\n' "$1"; }
|
|
222
|
+
t_bad() { printf ' ✗ %s\n' "$1"; fails=$((fails+1)); }
|
|
223
|
+
|
|
224
|
+
# A scratch repo, so the checks assert on a history they built themselves
|
|
225
|
+
# rather than on whatever this repo happens to look like today.
|
|
226
|
+
git init -q "$tmp/r" 2>/dev/null
|
|
227
|
+
(
|
|
228
|
+
cd "$tmp/r" || exit 1
|
|
229
|
+
git config user.email t@t; git config user.name t; git config commit.gpgsign false
|
|
230
|
+
echo base > a.txt; git add a.txt; git commit -qm "base commit"
|
|
231
|
+
git checkout -q -b feat/scratch
|
|
232
|
+
echo branch > b.txt; git add b.txt; git commit -qm "the branch's own first commit"
|
|
233
|
+
git checkout -q master 2>/dev/null || git checkout -q main
|
|
234
|
+
echo trunk >> a.txt; git add a.txt; git commit -qm "trunk moved on"
|
|
235
|
+
)
|
|
236
|
+
local basebr; basebr="$(cd "$tmp/r" && git rev-parse --abbrev-ref HEAD)"
|
|
237
|
+
local out
|
|
238
|
+
out="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" feat/scratch --base "$basebr" 2>/dev/null)"
|
|
239
|
+
|
|
240
|
+
# 1. The diff is merge-base scoped: the branch added b.txt and NOTHING else.
|
|
241
|
+
# A two-dot BASE..BR diff would also show a.txt as reverted.
|
|
242
|
+
grep -q 'b.txt' <<<"$out" && ! grep -q 'a.txt' <<<"$out" \
|
|
243
|
+
&& t_ok "diff is merge-base scoped (trunk's own commit excluded)" \
|
|
244
|
+
|| t_bad "diff leaked trunk's changes — merge-base scoping is broken"
|
|
245
|
+
|
|
246
|
+
# 2. Trunk drift is reported, not silently folded into the diff.
|
|
247
|
+
grep -q 'Trunk advanced during build:\*\* 1 commit' <<<"$out" \
|
|
248
|
+
&& t_ok "trunk drift counted" || t_bad "trunk drift not reported"
|
|
249
|
+
|
|
250
|
+
# 3. With no plan file and no worktree, the absent measurements are ABSENT —
|
|
251
|
+
# not rendered as 0 or "?". This is the claim that stops a body from
|
|
252
|
+
# reporting "Cycles closed: 0" about a branch that never had cycles.
|
|
253
|
+
! grep -qE 'Cycles closed|Rubric:|Open improvements' <<<"$out" \
|
|
254
|
+
&& t_ok "absent measurements are omitted, not zeroed" \
|
|
255
|
+
|| t_bad "rendered a measurement it never took"
|
|
256
|
+
|
|
257
|
+
# 4. No --gate ⇒ no gate line. This script runs no gate and must not imply it.
|
|
258
|
+
! grep -q 'Gate:' <<<"$out" && t_ok "claims no gate it did not run" || t_bad "invented a gate result"
|
|
259
|
+
|
|
260
|
+
# Same for --dev. A body that implies the branch was run on dev when nobody
|
|
261
|
+
# shipped it is the same dishonesty as an invented gate, one door later.
|
|
262
|
+
! grep -q 'Ran on dev:' <<<"$out" \
|
|
263
|
+
&& t_ok "claims no dev run it did not do" || t_bad "invented a dev run"
|
|
264
|
+
outd="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" feat/scratch --base "$basebr" --dev "https://dev.one.ie — 1 route proven" 2>/dev/null)"
|
|
265
|
+
grep -q 'Ran on dev:\*\* https://dev.one.ie' <<<"$outd" \
|
|
266
|
+
&& t_ok "reports the dev run it was handed" || t_bad "dropped the caller's dev result"
|
|
267
|
+
local outg
|
|
268
|
+
outg="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" feat/scratch --base "$basebr" --gate "fast lane (green)" 2>/dev/null)"
|
|
269
|
+
grep -q 'Gate:\*\* fast lane (green)' <<<"$outg" \
|
|
270
|
+
&& t_ok "reports the gate it was handed" || t_bad "dropped the caller's gate result"
|
|
271
|
+
|
|
272
|
+
# 5. Title falls back to the branch's FIRST commit, not its last.
|
|
273
|
+
local tl
|
|
274
|
+
tl="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" feat/scratch --base "$basebr" --title 2>/dev/null)"
|
|
275
|
+
[ "$tl" = "the branch's own first commit" ] \
|
|
276
|
+
&& t_ok "title falls back to the branch's first commit" \
|
|
277
|
+
|| t_bad "title fallback wrong: got '$tl'"
|
|
278
|
+
|
|
279
|
+
# 6. A conflicting branch is reported as conflicting. Without this the
|
|
280
|
+
# mergeability line is a constant that always says yes.
|
|
281
|
+
(
|
|
282
|
+
cd "$tmp/r" || exit 1
|
|
283
|
+
git checkout -q -b feat/conflict "$basebr"
|
|
284
|
+
printf 'totally different\n' > a.txt; git add a.txt; git commit -qm "rewrite a.txt"
|
|
285
|
+
git checkout -q "$basebr"
|
|
286
|
+
printf 'trunk rewrote it too\n' > a.txt; git add a.txt; git commit -qm "trunk rewrote a.txt"
|
|
287
|
+
)
|
|
288
|
+
local outc
|
|
289
|
+
outc="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" feat/conflict --base "$basebr" 2>/dev/null)"
|
|
290
|
+
grep -q 'Merges cleanly into .*:\*\* NO' <<<"$outc" \
|
|
291
|
+
&& t_ok "a conflicting branch reads NO" || t_bad "conflict not detected — the line is a constant"
|
|
292
|
+
grep -q 'Merges cleanly into .*:\*\* yes' <<<"$out" \
|
|
293
|
+
&& t_ok "a clean branch reads yes" || t_bad "clean branch misreported as conflicting"
|
|
294
|
+
|
|
295
|
+
# 7. The plan is read from the BRANCH, not from the checked-out tree. This is
|
|
296
|
+
# the bug the throwaway PR proof caught: reading $ROOT silently dropped the
|
|
297
|
+
# title, the cycle counts and the kill-switch, and the body still looked
|
|
298
|
+
# complete, because every one of those lines is optional. Non-vacuous by
|
|
299
|
+
# construction — the plan exists ONLY on the branch, never in the tree.
|
|
300
|
+
(
|
|
301
|
+
cd "$tmp/r" || exit 1
|
|
302
|
+
git switch -q -c do/planned "$basebr"
|
|
303
|
+
mkdir -p text
|
|
304
|
+
printf -- '---\ntitle: The Plan Name\nslug: planned\noutcome: "true"\n---\n\n- [x] C1 one\n- [ ] C2 two\n' > text/planned-todo.md
|
|
305
|
+
git add text/planned-todo.md; git commit -qm "add the plan"
|
|
306
|
+
git switch -q "$basebr"
|
|
307
|
+
)
|
|
308
|
+
local outp tlp
|
|
309
|
+
[ ! -f "$tmp/r/text/planned-todo.md" ] \
|
|
310
|
+
&& t_ok "the plan is absent from the working tree (check is non-vacuous)" \
|
|
311
|
+
|| t_bad "self-test setup wrong — plan is in the tree, so this proves nothing"
|
|
312
|
+
outp="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" do/planned --base "$basebr" 2>/dev/null)"
|
|
313
|
+
grep -q 'Cycles closed:\*\* 1 of 2' <<<"$outp" \
|
|
314
|
+
&& t_ok "cycle counts read from the branch's own plan" \
|
|
315
|
+
|| t_bad "cycle counts not read from the branch"
|
|
316
|
+
grep -q 'kill-switch' <<<"$outp" \
|
|
317
|
+
&& t_ok "kill-switch read from the branch's own plan" \
|
|
318
|
+
|| t_bad "kill-switch not read from the branch"
|
|
319
|
+
tlp="$(PR_BODY_ROOT="$tmp/r" bash "${BASH_SOURCE[0]}" do/planned --base "$basebr" --title 2>/dev/null)"
|
|
320
|
+
[ "$tlp" = "The Plan Name" ] \
|
|
321
|
+
&& t_ok "title read from the branch's own plan" \
|
|
322
|
+
|| t_bad "title not read from the branch: got '$tlp'"
|
|
323
|
+
|
|
324
|
+
printf '\n'
|
|
325
|
+
[ "$fails" -eq 0 ] && { echo "pr-body.sh: all checks pass"; return 0; }
|
|
326
|
+
echo "pr-body.sh: ${fails} check(s) FAILED"; return 1
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if [ "$SELF_TEST" = "1" ]; then self_test || exit 4; exit 0; fi
|
|
330
|
+
|
|
331
|
+
[ -n "$BR" ] || { echo "pr-body.sh: name a branch" >&2; exit 2; }
|
|
332
|
+
git -C "$ROOT" show-ref --verify --quiet "refs/heads/$BR" \
|
|
333
|
+
|| { echo "pr-body.sh: no such branch '$BR'" >&2; exit 3; }
|
|
334
|
+
|
|
335
|
+
emit "$BR" "$BASE"
|