create-egregore 0.19.33 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +141 -14
- package/bin/cli.js +19 -3
- package/lib/connect-runtime.js +4 -2
- package/lib/local.js +15 -1
- package/lib/prime-runtime.js +208 -0
- package/lib/setup.js +66 -5
- package/package.json +4 -3
- package/runtime/codex/.codex/config.toml +3 -0
- package/runtime/codex/.codex/skills/archive/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/branch/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/checkup/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/commit/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/contribute/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/delete-user/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/ingest/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/issue/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/meeting/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/note/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/onboarding/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/pr/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/push/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/reflect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/review-pr/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/save/SKILL.md +3 -1
- package/runtime/codex/.codex/skills/slack-connect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/sync-repos/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/tutorial/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/update/SKILL.md +1 -1
- package/runtime/codex/.codex/spec-manifest.json +5 -5
- package/runtime/codex/AGENTS.md +41 -35
- package/runtime/codex/bin/activity-data.sh +4 -8
- package/runtime/codex/bin/agent.sh +17 -5
- package/runtime/codex/bin/attendant.sh +13 -0
- package/runtime/codex/bin/autosave.sh +27 -1
- package/runtime/codex/bin/codex-render-spec.mjs +33 -32
- package/runtime/codex/bin/codex-session-start.sh +3 -1
- package/runtime/codex/bin/codex-sync-skills.sh +6 -1
- package/runtime/codex/bin/graph-batch.sh +25 -6
- package/runtime/codex/bin/graph-op.sh +5 -4
- package/runtime/codex/bin/graph.sh +31 -5
- package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/codex/bin/handoff-run.sh +8 -3
- package/runtime/codex/bin/handoff-save-egregore.sh +19 -7
- package/runtime/codex/bin/ingest.py +341 -10
- package/runtime/codex/bin/lib/context.sh +26 -1
- package/runtime/codex/bin/lib/git-message.sh +37 -0
- package/runtime/codex/bin/lib/git-sync.sh +10 -2
- package/runtime/codex/bin/lib/greeting.sh +4 -1
- package/runtime/codex/bin/lib/worktree-links.sh +25 -0
- package/runtime/codex/bin/session-autosave.sh +7 -1
- package/runtime/codex/bin/settings.sh +1 -1
- package/runtime/codex/manifest.json +10 -1
- package/runtime/pi/.codex/config.toml +3 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/branch/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/checkup/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/commit/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/contribute/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/ingest/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/issue/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/meeting/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/note/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/pr/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/push/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/reflect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/save/SKILL.md +3 -1
- package/runtime/pi/.codex/skills/slack-connect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/update/SKILL.md +1 -1
- package/runtime/pi/.codex/spec-manifest.json +5 -5
- package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -33
- package/runtime/pi/.pi/extensions/egregore.ts +17 -6
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +41 -35
- package/runtime/pi/bin/activity-data.sh +4 -8
- package/runtime/pi/bin/agent.sh +17 -5
- package/runtime/pi/bin/attendant.sh +13 -0
- package/runtime/pi/bin/autosave.sh +27 -1
- package/runtime/pi/bin/codex-render-spec.mjs +33 -32
- package/runtime/pi/bin/codex-session-start.sh +3 -1
- package/runtime/pi/bin/codex-sync-skills.sh +6 -1
- package/runtime/pi/bin/graph-batch.sh +25 -6
- package/runtime/pi/bin/graph-op.sh +5 -4
- package/runtime/pi/bin/graph.sh +31 -5
- package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/pi/bin/handoff-run.sh +8 -3
- package/runtime/pi/bin/handoff-save-egregore.sh +19 -7
- package/runtime/pi/bin/ingest.py +341 -10
- package/runtime/pi/bin/lib/context.sh +26 -1
- package/runtime/pi/bin/lib/git-message.sh +37 -0
- package/runtime/pi/bin/lib/git-sync.sh +10 -2
- package/runtime/pi/bin/lib/greeting.sh +4 -1
- package/runtime/pi/bin/lib/worktree-links.sh +25 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
- package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
- package/runtime/pi/bin/pi-render-spec.mjs +52 -26
- package/runtime/pi/bin/session-autosave.sh +7 -1
- package/runtime/pi/bin/settings.sh +1 -1
- package/runtime/pi/manifest.json +10 -1
- package/runtime/prime/.claude/context/notification-consent.md +66 -0
- package/runtime/prime/.codex/config.toml +20 -0
- package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
- package/runtime/prime/.codex/hooks/search-hint.js +67 -0
- package/runtime/prime/.codex/hooks.json +30 -0
- package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
- package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
- package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
- package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
- package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
- package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
- package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
- package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
- package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/save/SKILL.md +127 -0
- package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
- package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
- package/runtime/prime/.codex/spec-manifest.json +132 -0
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +416 -0
- package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
- package/runtime/prime/.prime/agent/settings.json +4 -0
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
- package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
- package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
- package/runtime/prime/AGENTS.md +488 -0
- package/runtime/prime/bin/activity-data.sh +238 -0
- package/runtime/prime/bin/agent.sh +787 -0
- package/runtime/prime/bin/attendant.sh +707 -0
- package/runtime/prime/bin/autosave.sh +112 -0
- package/runtime/prime/bin/capture-reconcile.sh +69 -0
- package/runtime/prime/bin/capture-run.sh +341 -0
- package/runtime/prime/bin/codex-install-skills.sh +78 -0
- package/runtime/prime/bin/codex-render-spec.mjs +435 -0
- package/runtime/prime/bin/codex-session-start.sh +265 -0
- package/runtime/prime/bin/codex-skill-render.mjs +347 -0
- package/runtime/prime/bin/codex-sync-skills.sh +237 -0
- package/runtime/prime/bin/connect-refresh.sh +33 -0
- package/runtime/prime/bin/dashboard-data.sh +178 -0
- package/runtime/prime/bin/graph-batch.sh +79 -0
- package/runtime/prime/bin/graph-op.sh +1102 -0
- package/runtime/prime/bin/graph-wal.sh +229 -0
- package/runtime/prime/bin/graph.sh +223 -0
- package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/prime/bin/handoff-run.sh +676 -0
- package/runtime/prime/bin/handoff-save-egregore.sh +224 -0
- package/runtime/prime/bin/index-handoff.sh +322 -0
- package/runtime/prime/bin/ingest-graph.sh +80 -0
- package/runtime/prime/bin/ingest-surface.html +495 -0
- package/runtime/prime/bin/ingest.py +1011 -0
- package/runtime/prime/bin/ingest.sh +279 -0
- package/runtime/prime/bin/ingest_graph.py +856 -0
- package/runtime/prime/bin/ingest_surface.py +544 -0
- package/runtime/prime/bin/lib/artifact-id.sh +46 -0
- package/runtime/prime/bin/lib/config.sh +116 -0
- package/runtime/prime/bin/lib/context.sh +621 -0
- package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
- package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
- package/runtime/prime/bin/lib/git-message.sh +37 -0
- package/runtime/prime/bin/lib/git-sync.sh +414 -0
- package/runtime/prime/bin/lib/greeting.sh +519 -0
- package/runtime/prime/bin/lib/hash.sh +13 -0
- package/runtime/prime/bin/lib/identity.sh +213 -0
- package/runtime/prime/bin/lib/metrics.sh +374 -0
- package/runtime/prime/bin/lib/noncode.sh +31 -0
- package/runtime/prime/bin/lib/permissions.sh +106 -0
- package/runtime/prime/bin/lib/time.sh +56 -0
- package/runtime/prime/bin/lib/worktree-links.sh +79 -0
- package/runtime/prime/bin/notify.sh +636 -0
- package/runtime/prime/bin/person.py +630 -0
- package/runtime/prime/bin/person.sh +448 -0
- package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
- package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
- package/runtime/prime/bin/prime-render-spec.mjs +147 -0
- package/runtime/prime/bin/prime-session-start.sh +24 -0
- package/runtime/prime/bin/publish-artifact.sh +271 -0
- package/runtime/prime/bin/publish-references.sh +144 -0
- package/runtime/prime/bin/repo-state.sh +96 -0
- package/runtime/prime/bin/search.sh +240 -0
- package/runtime/prime/bin/session-autosave.sh +215 -0
- package/runtime/prime/bin/session-log.sh +199 -0
- package/runtime/prime/bin/settings.sh +304 -0
- package/runtime/prime/bin/telemetry.sh +391 -0
- package/runtime/prime/bin/worktree-create.sh +128 -0
- package/runtime/prime/bin/worktree-remove.sh +50 -0
- package/runtime/prime/bin/worktree.sh +198 -0
- package/runtime/prime/manifest.json +181 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# identity.sh — User identity resolution for session-start.sh
|
|
3
|
+
#
|
|
4
|
+
# Resolves the current user's identity through multiple fallback layers:
|
|
5
|
+
# 1. .egregore-state.json github_username (stored during setup)
|
|
6
|
+
# 2. GitHub API auto-detect via GITHUB_TOKEN
|
|
7
|
+
# 3. Graph person lookup (existing team member on new machine)
|
|
8
|
+
# 4. Global git config user.name (last resort)
|
|
9
|
+
#
|
|
10
|
+
# Inputs: STATE_FILE, ENV_FILE, SCRIPT_DIR, HEALTH_GITHUB
|
|
11
|
+
# Outputs: AUTHOR, DISPLAY_NAME_STATE, FIRST_SESSION, HEALTH_GITHUB,
|
|
12
|
+
# EGREGORE_USER, EGREGORE_ORG, EGREGORE_SESSION_ID
|
|
13
|
+
# Writes .egregore-session-id and ~/.egregore/session-*.id
|
|
14
|
+
|
|
15
|
+
STORED_USERNAME=""
|
|
16
|
+
FIRST_SESSION=""
|
|
17
|
+
AUTHOR=""
|
|
18
|
+
if [ -f "$STATE_FILE" ]; then
|
|
19
|
+
STORED_USERNAME=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
DISPLAY_NAME_STATE=""
|
|
23
|
+
if [ -f "$STATE_FILE" ]; then
|
|
24
|
+
DISPLAY_NAME_STATE=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [ -n "$STORED_USERNAME" ]; then
|
|
28
|
+
# Identity stored during setup — use it and ensure repo-local git config matches
|
|
29
|
+
AUTHOR="$STORED_USERNAME"
|
|
30
|
+
HEALTH_GITHUB="ok"
|
|
31
|
+
CURRENT_LOCAL=$(git config --local user.name 2>/dev/null || echo "")
|
|
32
|
+
if [ "$CURRENT_LOCAL" != "$STORED_USERNAME" ]; then
|
|
33
|
+
STORED_NAME=$(jq -r '.github_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
34
|
+
git config user.name "${STORED_NAME:-$STORED_USERNAME}" 2>/dev/null || true
|
|
35
|
+
git config user.email "${STORED_USERNAME}@users.noreply.github.com" 2>/dev/null || true
|
|
36
|
+
fi
|
|
37
|
+
else
|
|
38
|
+
# No stored identity — try GitHub API to auto-detect (self-healing for pre-fix installs)
|
|
39
|
+
if [ -f "$ENV_FILE" ]; then
|
|
40
|
+
GH_TOKEN=$(grep '^GITHUB_TOKEN=' "$ENV_FILE" 2>/dev/null | cut -d'=' -f2-)
|
|
41
|
+
if [ -n "$GH_TOKEN" ]; then
|
|
42
|
+
GH_USER_JSON=$(curl -s -H "Authorization: token $GH_TOKEN" https://api.github.com/user --max-time 5 2>/dev/null || echo "")
|
|
43
|
+
GH_LOGIN=$(echo "$GH_USER_JSON" | jq -r '.login // empty' 2>/dev/null)
|
|
44
|
+
GH_ID=$(echo "$GH_USER_JSON" | jq -r '.id // empty' 2>/dev/null)
|
|
45
|
+
GH_NAME=$(echo "$GH_USER_JSON" | jq -r '.name // empty' 2>/dev/null)
|
|
46
|
+
GH_EMAIL=$(echo "$GH_USER_JSON" | jq -r '.email // empty' 2>/dev/null)
|
|
47
|
+
if [ -n "$GH_LOGIN" ]; then
|
|
48
|
+
GH_ID_JSON="null"
|
|
49
|
+
GH_PERSON_ID="github-login:$(echo "$GH_LOGIN" | tr '[:upper:]' '[:lower:]')"
|
|
50
|
+
if [[ "$GH_ID" =~ ^[0-9]+$ ]]; then
|
|
51
|
+
GH_ID_JSON="$GH_ID"
|
|
52
|
+
GH_PERSON_ID="github:$GH_ID"
|
|
53
|
+
fi
|
|
54
|
+
AUTHOR="$GH_LOGIN"
|
|
55
|
+
HEALTH_GITHUB="ok"
|
|
56
|
+
# Set repo-local config and save to state for next time
|
|
57
|
+
git config user.name "${GH_NAME:-$GH_LOGIN}" 2>/dev/null || true
|
|
58
|
+
git config user.email "${GH_LOGIN}@users.noreply.github.com" 2>/dev/null || true
|
|
59
|
+
# Save to state file so we don't need API call next time
|
|
60
|
+
# Include onboarding_complete + name so it doesn't re-trigger onboarding
|
|
61
|
+
# Determine if founder or joiner: if github_username != github_org, they're a joiner
|
|
62
|
+
GITHUB_ORG_CFG=$(jq -r '.github_org // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
63
|
+
if [ -n "$GITHUB_ORG_CFG" ] && [ "$GH_LOGIN" != "$GITHUB_ORG_CFG" ]; then
|
|
64
|
+
USAGE_TYPE="joiner_group"
|
|
65
|
+
else
|
|
66
|
+
USAGE_TYPE="founder_group"
|
|
67
|
+
fi
|
|
68
|
+
if [ -f "$STATE_FILE" ]; then
|
|
69
|
+
# Existing state file — update identity but preserve onboarding status
|
|
70
|
+
HAS_ONBOARDING=$(jq -r '.onboarding_complete // "unset"' "$STATE_FILE" 2>/dev/null)
|
|
71
|
+
if [ "$HAS_ONBOARDING" = "unset" ]; then
|
|
72
|
+
# State file exists but no onboarding flag — set to false to trigger it
|
|
73
|
+
jq --arg u "$GH_LOGIN" --arg n "${GH_NAME:-$GH_LOGIN}" --arg ut "$USAGE_TYPE" \
|
|
74
|
+
--argjson gid "$GH_ID_JSON" --arg pid "$GH_PERSON_ID" --arg email "$GH_EMAIL" \
|
|
75
|
+
'.github_username = $u | .github_id = $gid | .person_id = $pid |
|
|
76
|
+
.github_name = $n | .name = $n |
|
|
77
|
+
(if $email != "" then .email = ($email | ascii_downcase) else . end) |
|
|
78
|
+
.onboarding_complete = false | .usage_type = $ut' "$STATE_FILE" > "$STATE_FILE.tmp" \
|
|
79
|
+
&& mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
80
|
+
else
|
|
81
|
+
jq --arg u "$GH_LOGIN" --arg n "${GH_NAME:-$GH_LOGIN}" --arg ut "$USAGE_TYPE" \
|
|
82
|
+
--argjson gid "$GH_ID_JSON" --arg pid "$GH_PERSON_ID" --arg email "$GH_EMAIL" \
|
|
83
|
+
'.github_username = $u | .github_id = $gid | .person_id = $pid |
|
|
84
|
+
.github_name = $n | .name = $n |
|
|
85
|
+
(if $email != "" then .email = ($email | ascii_downcase) else . end) |
|
|
86
|
+
.usage_type = $ut' "$STATE_FILE" > "$STATE_FILE.tmp" \
|
|
87
|
+
&& mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
88
|
+
fi
|
|
89
|
+
FIRST_SESSION="false"
|
|
90
|
+
else
|
|
91
|
+
# No state file — check graph to see if this person already exists
|
|
92
|
+
# (handles fresh clone / new machine for existing team members)
|
|
93
|
+
GRAPH_PERSON=""
|
|
94
|
+
GRAPH_DISPLAY_NAME=""
|
|
95
|
+
GRAPH_PERSON=$(bash "$SCRIPT_DIR/bin/graph.sh" query \
|
|
96
|
+
"MATCH (p:Person)
|
|
97
|
+
WHERE NOT coalesce(p.kind, '') IN ['external', 'identity_alias']
|
|
98
|
+
AND coalesce(p.status, 'active') = 'active'
|
|
99
|
+
AND p.ingestRef IS NULL
|
|
100
|
+
AND (p.github = \$github
|
|
101
|
+
OR (\$githubId <> '' AND coalesce(toString(p.githubId), '') = \$githubId)
|
|
102
|
+
OR toLower(\$github) IN [alias IN coalesce(p.githubAliases, []) | toLower(alias)])
|
|
103
|
+
RETURN p.name AS name" \
|
|
104
|
+
"$(jq -n --arg github "$GH_LOGIN" --arg githubId "$GH_ID" \
|
|
105
|
+
'{github: $github, githubId: $githubId}')" 2>/dev/null || echo "")
|
|
106
|
+
if echo "$GRAPH_PERSON" | jq -e '.values | length > 0' &>/dev/null; then
|
|
107
|
+
GRAPH_DISPLAY_NAME=$(echo "$GRAPH_PERSON" | jq -r '.values[0][0] // empty' 2>/dev/null)
|
|
108
|
+
# Existing team member — skip onboarding
|
|
109
|
+
jq -n --arg github "$GH_LOGIN" --argjson githubId "$GH_ID_JSON" \
|
|
110
|
+
--arg personId "$GH_PERSON_ID" --arg githubName "${GH_NAME:-$GH_LOGIN}" \
|
|
111
|
+
--arg name "${GRAPH_DISPLAY_NAME:-${GH_NAME:-$GH_LOGIN}}" \
|
|
112
|
+
--arg displayName "${GRAPH_DISPLAY_NAME:-}" --arg email "$GH_EMAIL" \
|
|
113
|
+
--arg usageType "$USAGE_TYPE" \
|
|
114
|
+
'{
|
|
115
|
+
github_username: $github, github_id: $githubId, person_id: $personId,
|
|
116
|
+
github_name: $githubName, name: $name, display_name: $displayName,
|
|
117
|
+
email: (if $email == "" then null else ($email | ascii_downcase) end),
|
|
118
|
+
onboarding_complete: true, usage_type: $usageType,
|
|
119
|
+
session_tracking: true, transcript_sharing: true, telemetry: true,
|
|
120
|
+
contact_preference: "all", telemetry_noticed: true
|
|
121
|
+
}' > "$STATE_FILE"
|
|
122
|
+
FIRST_SESSION="false"
|
|
123
|
+
else
|
|
124
|
+
# Genuinely new user — trigger onboarding
|
|
125
|
+
jq -n --arg github "$GH_LOGIN" --argjson githubId "$GH_ID_JSON" \
|
|
126
|
+
--arg personId "$GH_PERSON_ID" --arg githubName "${GH_NAME:-$GH_LOGIN}" \
|
|
127
|
+
--arg email "$GH_EMAIL" --arg usageType "$USAGE_TYPE" \
|
|
128
|
+
'{
|
|
129
|
+
github_username: $github, github_id: $githubId, person_id: $personId,
|
|
130
|
+
github_name: $githubName, name: $githubName,
|
|
131
|
+
email: (if $email == "" then null else ($email | ascii_downcase) end),
|
|
132
|
+
onboarding_complete: false, usage_type: $usageType, first_session: true
|
|
133
|
+
}' > "$STATE_FILE"
|
|
134
|
+
FIRST_SESSION="true"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Final fallback: git config user.name (global or local)
|
|
142
|
+
if [ -z "$AUTHOR" ]; then
|
|
143
|
+
FULLNAME=$(git config user.name 2>/dev/null || echo "")
|
|
144
|
+
AUTHOR=$(echo "$FULLNAME" | tr '[:upper:]' '[:lower:]' | cut -d' ' -f1)
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
if [ -z "$AUTHOR" ]; then
|
|
149
|
+
AUTHOR="unknown"
|
|
150
|
+
HEALTH_GITHUB="fail"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# --- Validate gh CLI identity matches git identity ---
|
|
154
|
+
# The gh CLI has its own auth (keyring) separate from .env GITHUB_TOKEN.
|
|
155
|
+
# If they diverge, PRs get created under the wrong account.
|
|
156
|
+
# The `gh api user` round-trip (~1s) is off the boot path: each start reports
|
|
157
|
+
# the marker the previous background check left, then refreshes it detached.
|
|
158
|
+
# A mismatch therefore surfaces one session late — acceptable for a warning
|
|
159
|
+
# about a rare, persistent condition.
|
|
160
|
+
if command -v gh &>/dev/null && [ -n "$AUTHOR" ] && [ "$AUTHOR" != "unknown" ]; then
|
|
161
|
+
_GH_MISMATCH_FILE="$HOME/.egregore/gh-identity-mismatch-$(echo -n "${MAIN_PROJECT_DIR:-$SCRIPT_DIR}" | cksum | cut -d' ' -f1)"
|
|
162
|
+
# Marker format: line 1 = the AUTHOR the check ran as, line 2 = the gh login
|
|
163
|
+
# observed. Warn only when line 1 matches the current AUTHOR — the same
|
|
164
|
+
# machine/instance can serve different git identities, and Alice's stale
|
|
165
|
+
# marker must not tell Bob that Bob mismatches Bob.
|
|
166
|
+
_GH_MARKER_AUTHOR=$(sed -n '1p' "$_GH_MISMATCH_FILE" 2>/dev/null || true)
|
|
167
|
+
GH_CLI_USER=$(sed -n '2p' "$_GH_MISMATCH_FILE" 2>/dev/null || true)
|
|
168
|
+
if [ -n "$GH_CLI_USER" ] && [ "$_GH_MARKER_AUTHOR" = "$AUTHOR" ]; then
|
|
169
|
+
echo ""
|
|
170
|
+
echo "WARNING: Identity mismatch detected."
|
|
171
|
+
echo " Git identity (.env): $AUTHOR"
|
|
172
|
+
echo " gh CLI (active): $GH_CLI_USER"
|
|
173
|
+
echo " PRs will be created as '$GH_CLI_USER', not '$AUTHOR'."
|
|
174
|
+
echo " Fix: gh auth switch --user $AUTHOR"
|
|
175
|
+
echo ""
|
|
176
|
+
fi
|
|
177
|
+
( (
|
|
178
|
+
_GH_LIVE=$(gh api user --jq '.login' 2>/dev/null || true)
|
|
179
|
+
if [ -n "$_GH_LIVE" ]; then
|
|
180
|
+
if [ "$(echo "$_GH_LIVE" | tr '[:upper:]' '[:lower:]')" != "$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]')" ]; then
|
|
181
|
+
mkdir -p "$HOME/.egregore" 2>/dev/null
|
|
182
|
+
printf '%s\n%s\n' "$AUTHOR" "$_GH_LIVE" > "$_GH_MISMATCH_FILE.tmp" \
|
|
183
|
+
&& mv "$_GH_MISMATCH_FILE.tmp" "$_GH_MISMATCH_FILE"
|
|
184
|
+
else
|
|
185
|
+
rm -f "$_GH_MISMATCH_FILE"
|
|
186
|
+
fi
|
|
187
|
+
fi
|
|
188
|
+
) >/dev/null 2>&1 & ) 2>/dev/null
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# --- Export telemetry identity env vars ---
|
|
192
|
+
export EGREGORE_USER="$AUTHOR"
|
|
193
|
+
export EGREGORE_ORG="$(jq -r '.slug // .github_org // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null || true)"
|
|
194
|
+
export EGREGORE_SESSION_ID="$(date -u +%Y%m%dT%H%M%S)-${AUTHOR}-$$"
|
|
195
|
+
|
|
196
|
+
# Persist session ID to file so telemetry.sh can read it
|
|
197
|
+
# (env vars from hooks don't propagate into the Claude Code agent)
|
|
198
|
+
SESSION_ID_DIR="$HOME/.egregore"
|
|
199
|
+
mkdir -p "$SESSION_ID_DIR"
|
|
200
|
+
PROJ_HASH=$(echo -n "$SCRIPT_DIR" | md5 2>/dev/null || echo -n "$SCRIPT_DIR" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
201
|
+
echo "$EGREGORE_SESSION_ID" > "$SESSION_ID_DIR/session-${PROJ_HASH}.id"
|
|
202
|
+
# Also write project-local file (CLAUDE.md reads this — no glob needed)
|
|
203
|
+
echo "$EGREGORE_SESSION_ID" > "$SCRIPT_DIR/.egregore-session-id"
|
|
204
|
+
|
|
205
|
+
# Model stamp hashes the git common-dir root because telemetry.sh resolves it that way.
|
|
206
|
+
MODEL_REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --path-format=absolute --git-common-dir 2>/dev/null | sed 's|/\.git$||') || MODEL_REPO_ROOT="$SCRIPT_DIR"
|
|
207
|
+
MODEL_PROJ_HASH=$(echo -n "$MODEL_REPO_ROOT" | md5 2>/dev/null || echo -n "$MODEL_REPO_ROOT" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
208
|
+
MODEL_STAMP_FILE="$SESSION_ID_DIR/session-model-${MODEL_PROJ_HASH}"
|
|
209
|
+
if [ -n "${CLAUDE_MODEL:-}" ]; then
|
|
210
|
+
echo "$CLAUDE_MODEL" > "$MODEL_STAMP_FILE"
|
|
211
|
+
else
|
|
212
|
+
rm -f "$MODEL_STAMP_FILE"
|
|
213
|
+
fi
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# metrics.sh — Momentum metrics gathering and rendering for startup greetings.
|
|
3
|
+
#
|
|
4
|
+
# Function-only library. Safe to source from bash or zsh.
|
|
5
|
+
|
|
6
|
+
_metrics_zero_json() {
|
|
7
|
+
printf '%s\n' '{"source":"local","sessions":{"buckets":[0,0,0,0,0],"week":0,"prev":0,"all":0},"commits":{"buckets":[0,0,0,0,0],"week":0,"prev":0,"authors":0,"all":0},"handoffs":{"buckets":[0,0,0,0,0],"week":0,"prev":0,"avg_resolution_days":null,"all":0},"knowledge":{"buckets":[0,0,0,0,0],"week":0,"prev":0,"all":0},"members":{"active":0,"total":0}}'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
_num() {
|
|
11
|
+
awk '{n=$1+0; if (n < 0) n=0; printf "%d\n", n}' 2>/dev/null <<EOF
|
|
12
|
+
$1
|
|
13
|
+
EOF
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_num_float() {
|
|
17
|
+
awk '{n=$1+0; if (n < 0) n=0; printf "%.1f\n", n}' 2>/dev/null <<EOF
|
|
18
|
+
$1
|
|
19
|
+
EOF
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_normalize_buckets() {
|
|
23
|
+
set -- $(printf '%s' "$1")
|
|
24
|
+
printf '%d %d %d %d %d\n' "$(_num "${1:-0}")" "$(_num "${2:-0}")" "$(_num "${3:-0}")" "$(_num "${4:-0}")" "$(_num "${5:-0}")"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_buckets_json() {
|
|
28
|
+
set -- $(printf '%s' "$(_normalize_buckets "$1")")
|
|
29
|
+
jq -n --argjson a "$(_num "${1:-0}")" --argjson b "$(_num "${2:-0}")" \
|
|
30
|
+
--argjson c "$(_num "${3:-0}")" --argjson d "$(_num "${4:-0}")" \
|
|
31
|
+
--argjson e "$(_num "${5:-0}")" '[$a,$b,$c,$d,$e]' 2>/dev/null || echo '[0,0,0,0,0]'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_bucket_week() {
|
|
35
|
+
set -- $(printf '%s' "$(_normalize_buckets "$1")")
|
|
36
|
+
_num "${5:-0}"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_bucket_prev() {
|
|
40
|
+
set -- $(printf '%s' "$(_normalize_buckets "$1")")
|
|
41
|
+
_num "${4:-0}"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_spark() {
|
|
45
|
+
local values max out v idx glyph
|
|
46
|
+
values="${1:-0 0 0 0 0}"
|
|
47
|
+
max=0
|
|
48
|
+
set -- $(printf '%s' "$values")
|
|
49
|
+
[ "$#" -eq 0 ] && set -- 0 0 0 0 0
|
|
50
|
+
for v in "$@"; do
|
|
51
|
+
v=$(_num "$v")
|
|
52
|
+
[ "$v" -gt "$max" ] 2>/dev/null && max="$v"
|
|
53
|
+
done
|
|
54
|
+
out=""
|
|
55
|
+
for v in "$@"; do
|
|
56
|
+
v=$(_num "$v")
|
|
57
|
+
if [ "$max" -le 0 ] 2>/dev/null; then
|
|
58
|
+
idx=0
|
|
59
|
+
else
|
|
60
|
+
idx=$(( (v * 7) / max ))
|
|
61
|
+
fi
|
|
62
|
+
# Select glyphs explicitly: substring slicing can split UTF-8 characters
|
|
63
|
+
# under Bash 3.2 or a C locale.
|
|
64
|
+
case "$idx" in
|
|
65
|
+
0) glyph="▁" ;;
|
|
66
|
+
1) glyph="▂" ;;
|
|
67
|
+
2) glyph="▃" ;;
|
|
68
|
+
3) glyph="▄" ;;
|
|
69
|
+
4) glyph="▅" ;;
|
|
70
|
+
5) glyph="▆" ;;
|
|
71
|
+
6) glyph="▇" ;;
|
|
72
|
+
*) glyph="█" ;;
|
|
73
|
+
esac
|
|
74
|
+
out="${out}${glyph}"
|
|
75
|
+
done
|
|
76
|
+
printf '%s\n' "$out"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_pct_delta() {
|
|
80
|
+
local cur prev pct
|
|
81
|
+
cur=$(_num "${1:-0}")
|
|
82
|
+
prev=$(_num "${2:-0}")
|
|
83
|
+
[ "$prev" -le 0 ] 2>/dev/null && return 0
|
|
84
|
+
pct=$(( ((cur - prev) * 100) / prev ))
|
|
85
|
+
if [ "$pct" -gt 0 ] 2>/dev/null; then
|
|
86
|
+
printf '↑ %d%%\n' "$pct"
|
|
87
|
+
elif [ "$pct" -lt 0 ] 2>/dev/null; then
|
|
88
|
+
printf '↓ %d%%\n' "$(( -pct ))"
|
|
89
|
+
else
|
|
90
|
+
printf '→ 0%%\n'
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_commafy() {
|
|
95
|
+
awk '
|
|
96
|
+
function comma(s, out, sign) {
|
|
97
|
+
s = int(s + 0)
|
|
98
|
+
if (s < 0) { sign = "-"; s = -s } else { sign = "" }
|
|
99
|
+
s = sprintf("%d", s)
|
|
100
|
+
out = ""
|
|
101
|
+
while (length(s) > 3) {
|
|
102
|
+
out = "," substr(s, length(s) - 2, 3) out
|
|
103
|
+
s = substr(s, 1, length(s) - 3)
|
|
104
|
+
}
|
|
105
|
+
return sign s out
|
|
106
|
+
}
|
|
107
|
+
{ print comma($1) }
|
|
108
|
+
' 2>/dev/null <<EOF
|
|
109
|
+
${1:-0}
|
|
110
|
+
EOF
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_git_added_buckets() {
|
|
114
|
+
local repo now paths
|
|
115
|
+
repo="$1"
|
|
116
|
+
shift
|
|
117
|
+
now=$(date +%s)
|
|
118
|
+
if [ -d "$repo" ] || [ -f "$repo" ]; then
|
|
119
|
+
git -C "$repo" log --diff-filter=A --since="35 days ago" --format='%ct' -- "$@" 2>/dev/null | \
|
|
120
|
+
awk -v now="$now" '
|
|
121
|
+
BEGIN { for (i=0;i<5;i++) b[i]=0 }
|
|
122
|
+
/^[0-9]+$/ {
|
|
123
|
+
days = int((now - $1) / 86400)
|
|
124
|
+
idx = 4 - int(days / 7)
|
|
125
|
+
if (idx >= 0 && idx < 5) b[idx]++
|
|
126
|
+
}
|
|
127
|
+
END { printf "%d %d %d %d %d\n", b[0], b[1], b[2], b[3], b[4] }
|
|
128
|
+
' 2>/dev/null || echo "0 0 0 0 0"
|
|
129
|
+
else
|
|
130
|
+
echo "0 0 0 0 0"
|
|
131
|
+
fi
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_find_count_md() {
|
|
135
|
+
find -L "$@" -name '*.md' ! -name 'index*' 2>/dev/null | wc -l | awk '{print $1+0}' 2>/dev/null || echo 0
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_metrics_gather() {
|
|
139
|
+
local _root _mem _scratch _API_URL _API_KEY _connected
|
|
140
|
+
_root="${SCRIPT_DIR:-$PWD}"
|
|
141
|
+
_mem="$_root/memory"
|
|
142
|
+
_scratch=$(mktemp -d 2>/dev/null || mktemp -d -t egregore-metrics)
|
|
143
|
+
_API_URL=$(jq -r '.api_url // empty' "$_root/egregore.json" 2>/dev/null || echo "")
|
|
144
|
+
_API_KEY=$(grep '^EGREGORE_API_KEY=' "${ENV_FILE:-$_root/.env}" 2>/dev/null | cut -d'=' -f2- || true)
|
|
145
|
+
_connected=false
|
|
146
|
+
[ -n "$_API_URL" ] && [ -n "$_API_KEY" ] && _connected=true
|
|
147
|
+
|
|
148
|
+
(
|
|
149
|
+
if [ "$_connected" = true ]; then
|
|
150
|
+
raw=$(bash "$_root/bin/graph.sh" query "MATCH (s:Session) WHERE date(s.date) >= date() - duration('P35D')
|
|
151
|
+
OPTIONAL MATCH (s)-[:BY]->(p:Person)
|
|
152
|
+
WITH duration.inDays(date(s.date), date()).days / 7 AS weeksAgo,
|
|
153
|
+
count(s) AS sessions, count(DISTINCT p) AS people
|
|
154
|
+
RETURN weeksAgo, sessions, people ORDER BY weeksAgo" 2>/dev/null || echo "")
|
|
155
|
+
printf '%s' "$raw" | jq -r '
|
|
156
|
+
(.values // []) as $v
|
|
157
|
+
| if ($v|length) == 0 then empty else
|
|
158
|
+
reduce $v[] as $r ({b:[0,0,0,0,0], a:0, ok:0};
|
|
159
|
+
($r[0] // 99 | tonumber? // 99) as $w
|
|
160
|
+
| if $w >= 0 and $w <= 4 then
|
|
161
|
+
.b[4 - $w] = (($r[1] // 0 | tonumber? // 0) | floor)
|
|
162
|
+
| .ok = 1
|
|
163
|
+
| if $w == 0 then .a = (($r[2] // 0 | tonumber? // 0) | floor) else . end
|
|
164
|
+
else . end)
|
|
165
|
+
| "\(.b|join(" "))|\(.a)|\(.ok)"
|
|
166
|
+
end
|
|
167
|
+
' 2>/dev/null || echo "0 0 0 0 0|0|0"
|
|
168
|
+
else
|
|
169
|
+
echo "0 0 0 0 0|0|0"
|
|
170
|
+
fi
|
|
171
|
+
) > "$_scratch/graph_sessions" 2>/dev/null &
|
|
172
|
+
|
|
173
|
+
(
|
|
174
|
+
if [ "$_connected" = true ]; then
|
|
175
|
+
raw=$(bash "$_root/bin/graph.sh" query "MATCH (s:Session) WITH count(s) AS totalSessions MATCH (p:Person)-[:MEMBER_OF]->(:Org) RETURN totalSessions, count(DISTINCT p) AS members" 2>/dev/null || echo "")
|
|
176
|
+
total=$(printf '%s' "$raw" | jq -r '.values[0][0] // 0' 2>/dev/null || echo 0)
|
|
177
|
+
members=$(printf '%s' "$raw" | jq -r '.values[0][1] // 0' 2>/dev/null || echo 0)
|
|
178
|
+
if [ "$(_num "$members")" -eq 0 ] 2>/dev/null; then
|
|
179
|
+
raw2=$(bash "$_root/bin/graph.sh" query "MATCH (p:Person) RETURN count(p)" 2>/dev/null || echo "")
|
|
180
|
+
members=$(printf '%s' "$raw2" | jq -r '.values[0][0] // 0' 2>/dev/null || echo 0)
|
|
181
|
+
fi
|
|
182
|
+
printf '%s|%s\n' "$(_num "$total")" "$(_num "$members")"
|
|
183
|
+
else
|
|
184
|
+
echo "0|0"
|
|
185
|
+
fi
|
|
186
|
+
) > "$_scratch/graph_totals" 2>/dev/null &
|
|
187
|
+
|
|
188
|
+
(
|
|
189
|
+
if [ "$_connected" = true ]; then
|
|
190
|
+
raw=$(bash "$_root/bin/graph.sh" query "MATCH (s:Session)-[:HANDED_TO]->(:Person)
|
|
191
|
+
WHERE s.handoffStatus = 'done' AND date(s.date) >= date() - duration('P30D')
|
|
192
|
+
AND s.handoffReadDate IS NOT NULL
|
|
193
|
+
WITH duration.inDays(date(s.date), date(s.handoffReadDate)).days AS d
|
|
194
|
+
RETURN avg(d) AS avgDays, count(*) AS n" 2>/dev/null || echo "")
|
|
195
|
+
avg=$(printf '%s' "$raw" | jq -r '.values[0][0] // empty' 2>/dev/null || echo "")
|
|
196
|
+
n=$(printf '%s' "$raw" | jq -r '.values[0][1] // 0' 2>/dev/null || echo 0)
|
|
197
|
+
if [ "$(_num "$n")" -gt 0 ] 2>/dev/null && [ -n "$avg" ]; then _num_float "$avg"; else echo ""; fi
|
|
198
|
+
else
|
|
199
|
+
echo ""
|
|
200
|
+
fi
|
|
201
|
+
) > "$_scratch/handoff_resolution" 2>/dev/null &
|
|
202
|
+
|
|
203
|
+
(
|
|
204
|
+
buckets=$(_git_added_buckets "$_mem" sessions wraps)
|
|
205
|
+
all=$(_find_count_md "$_root/memory/sessions" "$_root/memory/wraps")
|
|
206
|
+
printf '%s|%s\n' "$buckets" "$all"
|
|
207
|
+
) > "$_scratch/local_sessions" 2>/dev/null &
|
|
208
|
+
|
|
209
|
+
(
|
|
210
|
+
now=$(date +%s)
|
|
211
|
+
if [ -d "$_mem" ] || [ -f "$_mem" ]; then
|
|
212
|
+
git -C "$_mem" log --since="35 days ago" --format='%ct|%ae' 2>/dev/null | \
|
|
213
|
+
awk -F'|' -v now="$now" '
|
|
214
|
+
BEGIN { for (i=0;i<5;i++) b[i]=0 }
|
|
215
|
+
{
|
|
216
|
+
days = int((now - $1) / 86400)
|
|
217
|
+
idx = 4 - int(days / 7)
|
|
218
|
+
if (idx >= 0 && idx < 5) {
|
|
219
|
+
b[idx]++
|
|
220
|
+
if (idx == 4 && $2 != "") a[$2]=1
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
END {
|
|
224
|
+
n=0; for (k in a) n++
|
|
225
|
+
printf "%d %d %d %d %d|%d\n", b[0], b[1], b[2], b[3], b[4], n
|
|
226
|
+
}
|
|
227
|
+
' 2>/dev/null || echo "0 0 0 0 0|0"
|
|
228
|
+
else
|
|
229
|
+
echo "0 0 0 0 0|0"
|
|
230
|
+
fi
|
|
231
|
+
) > "$_scratch/commits" 2>/dev/null &
|
|
232
|
+
|
|
233
|
+
(
|
|
234
|
+
if [ -d "$_mem" ] || [ -f "$_mem" ]; then git -C "$_mem" rev-list --count HEAD 2>/dev/null || echo 0; else echo 0; fi
|
|
235
|
+
) > "$_scratch/commits_all" 2>/dev/null &
|
|
236
|
+
|
|
237
|
+
(
|
|
238
|
+
buckets=$(_git_added_buckets "$_mem" handoffs)
|
|
239
|
+
all=$(_find_count_md "$_root/memory/handoffs")
|
|
240
|
+
printf '%s|%s\n' "$buckets" "$all"
|
|
241
|
+
) > "$_scratch/handoffs" 2>/dev/null &
|
|
242
|
+
|
|
243
|
+
(
|
|
244
|
+
buckets=$(_git_added_buckets "$_mem" knowledge/decisions knowledge/findings knowledge/patterns)
|
|
245
|
+
all=$(_find_count_md "$_root/memory/knowledge/decisions" "$_root/memory/knowledge/findings" "$_root/memory/knowledge/patterns")
|
|
246
|
+
printf '%s|%s\n' "$buckets" "$all"
|
|
247
|
+
) > "$_scratch/knowledge" 2>/dev/null &
|
|
248
|
+
|
|
249
|
+
(
|
|
250
|
+
_find_count_md "$_root/memory/people"
|
|
251
|
+
) > "$_scratch/members_total_local" 2>/dev/null &
|
|
252
|
+
|
|
253
|
+
wait
|
|
254
|
+
|
|
255
|
+
gs=$(cat "$_scratch/graph_sessions" 2>/dev/null || echo "0 0 0 0 0|0|0")
|
|
256
|
+
gt=$(cat "$_scratch/graph_totals" 2>/dev/null || echo "0|0")
|
|
257
|
+
lsess=$(cat "$_scratch/local_sessions" 2>/dev/null || echo "0 0 0 0 0|0")
|
|
258
|
+
commits=$(cat "$_scratch/commits" 2>/dev/null || echo "0 0 0 0 0|0")
|
|
259
|
+
handoffs=$(cat "$_scratch/handoffs" 2>/dev/null || echo "0 0 0 0 0|0")
|
|
260
|
+
knowledge=$(cat "$_scratch/knowledge" 2>/dev/null || echo "0 0 0 0 0|0")
|
|
261
|
+
|
|
262
|
+
graph_valid=$(_num "$(printf '%s' "$gs" | awk -F'|' '{print $3}')")
|
|
263
|
+
if [ "$graph_valid" -gt 0 ] 2>/dev/null; then
|
|
264
|
+
source_name="graph"
|
|
265
|
+
session_buckets=$(_normalize_buckets "$(printf '%s' "$gs" | awk -F'|' '{print $1}')")
|
|
266
|
+
session_all=$(_num "$(printf '%s' "$gt" | awk -F'|' '{print $1}')")
|
|
267
|
+
members_active=$(_num "$(printf '%s' "$gs" | awk -F'|' '{print $2}')")
|
|
268
|
+
members_total=$(_num "$(printf '%s' "$gt" | awk -F'|' '{print $2}')")
|
|
269
|
+
if [ "$session_all" -le 0 ] 2>/dev/null; then
|
|
270
|
+
session_all=$(_num "$(printf '%s' "$lsess" | awk -F'|' '{print $2}')")
|
|
271
|
+
fi
|
|
272
|
+
if [ "$members_total" -le 0 ] 2>/dev/null; then
|
|
273
|
+
members_total=$(_num "$(cat "$_scratch/members_total_local" 2>/dev/null || echo 0)")
|
|
274
|
+
fi
|
|
275
|
+
else
|
|
276
|
+
source_name="local"
|
|
277
|
+
session_buckets=$(_normalize_buckets "$(printf '%s' "$lsess" | awk -F'|' '{print $1}')")
|
|
278
|
+
session_all=$(_num "$(printf '%s' "$lsess" | awk -F'|' '{print $2}')")
|
|
279
|
+
members_active=$(_num "$(printf '%s' "$commits" | awk -F'|' '{print $2}')")
|
|
280
|
+
members_total=$(_num "$(cat "$_scratch/members_total_local" 2>/dev/null || echo 0)")
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
commit_buckets=$(_normalize_buckets "$(printf '%s' "$commits" | awk -F'|' '{print $1}')")
|
|
284
|
+
handoff_buckets=$(_normalize_buckets "$(printf '%s' "$handoffs" | awk -F'|' '{print $1}')")
|
|
285
|
+
knowledge_buckets=$(_normalize_buckets "$(printf '%s' "$knowledge" | awk -F'|' '{print $1}')")
|
|
286
|
+
avg_res=$(cat "$_scratch/handoff_resolution" 2>/dev/null || echo "")
|
|
287
|
+
|
|
288
|
+
session_buckets_json=$(_buckets_json "$session_buckets")
|
|
289
|
+
commit_buckets_json=$(_buckets_json "$commit_buckets")
|
|
290
|
+
handoff_buckets_json=$(_buckets_json "$handoff_buckets")
|
|
291
|
+
knowledge_buckets_json=$(_buckets_json "$knowledge_buckets")
|
|
292
|
+
|
|
293
|
+
if [ -n "$avg_res" ]; then
|
|
294
|
+
jq -n --arg source "$source_name" \
|
|
295
|
+
--argjson sb "$session_buckets_json" --argjson sw "$(_bucket_week "$session_buckets")" --argjson sp "$(_bucket_prev "$session_buckets")" --argjson sall "$session_all" \
|
|
296
|
+
--argjson cb "$commit_buckets_json" --argjson cw "$(_bucket_week "$commit_buckets")" --argjson cp "$(_bucket_prev "$commit_buckets")" --argjson ca "$(_num "$(printf '%s' "$commits" | awk -F'|' '{print $2}')")" --argjson call "$(_num "$(cat "$_scratch/commits_all" 2>/dev/null || echo 0)")" \
|
|
297
|
+
--argjson hb "$handoff_buckets_json" --argjson hw "$(_bucket_week "$handoff_buckets")" --argjson hp "$(_bucket_prev "$handoff_buckets")" --argjson havg "$avg_res" --argjson hall "$(_num "$(printf '%s' "$handoffs" | awk -F'|' '{print $2}')")" \
|
|
298
|
+
--argjson kb "$knowledge_buckets_json" --argjson kw "$(_bucket_week "$knowledge_buckets")" --argjson kp "$(_bucket_prev "$knowledge_buckets")" --argjson kall "$(_num "$(printf '%s' "$knowledge" | awk -F'|' '{print $2}')")" \
|
|
299
|
+
--argjson ma "$members_active" --argjson mt "$members_total" \
|
|
300
|
+
'{source:$source,sessions:{buckets:$sb,week:$sw,prev:$sp,all:$sall},commits:{buckets:$cb,week:$cw,prev:$cp,authors:$ca,all:$call},handoffs:{buckets:$hb,week:$hw,prev:$hp,avg_resolution_days:$havg,all:$hall},knowledge:{buckets:$kb,week:$kw,prev:$kp,all:$kall},members:{active:$ma,total:$mt}}' 2>/dev/null || _metrics_zero_json
|
|
301
|
+
else
|
|
302
|
+
jq -n --arg source "$source_name" \
|
|
303
|
+
--argjson sb "$session_buckets_json" --argjson sw "$(_bucket_week "$session_buckets")" --argjson sp "$(_bucket_prev "$session_buckets")" --argjson sall "$session_all" \
|
|
304
|
+
--argjson cb "$commit_buckets_json" --argjson cw "$(_bucket_week "$commit_buckets")" --argjson cp "$(_bucket_prev "$commit_buckets")" --argjson ca "$(_num "$(printf '%s' "$commits" | awk -F'|' '{print $2}')")" --argjson call "$(_num "$(cat "$_scratch/commits_all" 2>/dev/null || echo 0)")" \
|
|
305
|
+
--argjson hb "$handoff_buckets_json" --argjson hw "$(_bucket_week "$handoff_buckets")" --argjson hp "$(_bucket_prev "$handoff_buckets")" --argjson hall "$(_num "$(printf '%s' "$handoffs" | awk -F'|' '{print $2}')")" \
|
|
306
|
+
--argjson kb "$knowledge_buckets_json" --argjson kw "$(_bucket_week "$knowledge_buckets")" --argjson kp "$(_bucket_prev "$knowledge_buckets")" --argjson kall "$(_num "$(printf '%s' "$knowledge" | awk -F'|' '{print $2}')")" \
|
|
307
|
+
--argjson ma "$members_active" --argjson mt "$members_total" \
|
|
308
|
+
'{source:$source,sessions:{buckets:$sb,week:$sw,prev:$sp,all:$sall},commits:{buckets:$cb,week:$cw,prev:$cp,authors:$ca,all:$call},handoffs:{buckets:$hb,week:$hw,prev:$hp,avg_resolution_days:null,all:$hall},knowledge:{buckets:$kb,week:$kw,prev:$kp,all:$kall},members:{active:$ma,total:$mt}}' 2>/dev/null || _metrics_zero_json
|
|
309
|
+
fi
|
|
310
|
+
rm -rf "$_scratch" 2>/dev/null || true
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_metrics_read() {
|
|
314
|
+
local file filter fallback
|
|
315
|
+
file="${CTX_DIR:-}/metrics"
|
|
316
|
+
filter="$1"
|
|
317
|
+
fallback="$2"
|
|
318
|
+
if [ -f "$file" ]; then
|
|
319
|
+
jq -r "$filter" "$file" 2>/dev/null || echo "$fallback"
|
|
320
|
+
else
|
|
321
|
+
echo "$fallback"
|
|
322
|
+
fi
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
_render_momentum_board() {
|
|
326
|
+
local separator line_width header_left header_right pad
|
|
327
|
+
local sb sw sp sall cb cw ca call hb hw havg hall kb kw kall active total
|
|
328
|
+
local sdelta sann cann hann kann know_week
|
|
329
|
+
separator="${SEPARATOR:- ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄}"
|
|
330
|
+
line_width="${LINE_WIDTH:-67}"
|
|
331
|
+
|
|
332
|
+
sb=$(_metrics_read '(.sessions.buckets // [0,0,0,0,0]) | .[0:5] | map(. // 0) | join(" ")' "0 0 0 0 0")
|
|
333
|
+
sw=$(_num "$(_metrics_read '.sessions.week // 0' "0")")
|
|
334
|
+
sp=$(_num "$(_metrics_read '.sessions.prev // 0' "0")")
|
|
335
|
+
sall=$(_num "$(_metrics_read '.sessions.all // 0' "0")")
|
|
336
|
+
cb=$(_metrics_read '(.commits.buckets // [0,0,0,0,0]) | .[0:5] | map(. // 0) | join(" ")' "0 0 0 0 0")
|
|
337
|
+
cw=$(_num "$(_metrics_read '.commits.week // 0' "0")")
|
|
338
|
+
ca=$(_num "$(_metrics_read '.commits.authors // 0' "0")")
|
|
339
|
+
call=$(_num "$(_metrics_read '.commits.all // 0' "0")")
|
|
340
|
+
hb=$(_metrics_read '(.handoffs.buckets // [0,0,0,0,0]) | .[0:5] | map(. // 0) | join(" ")' "0 0 0 0 0")
|
|
341
|
+
hw=$(_num "$(_metrics_read '.handoffs.week // 0' "0")")
|
|
342
|
+
havg=$(_metrics_read '.handoffs.avg_resolution_days // empty' "")
|
|
343
|
+
hall=$(_num "$(_metrics_read '.handoffs.all // 0' "0")")
|
|
344
|
+
kb=$(_metrics_read '(.knowledge.buckets // [0,0,0,0,0]) | .[0:5] | map(. // 0) | join(" ")' "0 0 0 0 0")
|
|
345
|
+
kw=$(_num "$(_metrics_read '.knowledge.week // 0' "0")")
|
|
346
|
+
kall=$(_num "$(_metrics_read '.knowledge.all // 0' "0")")
|
|
347
|
+
active=$(_num "$(_metrics_read '.members.active // 0' "0")")
|
|
348
|
+
total=$(_num "$(_metrics_read '.members.total // 0' "0")")
|
|
349
|
+
|
|
350
|
+
header_left=" ◦ momentum · this week"
|
|
351
|
+
header_right="${active} of ${total} active"
|
|
352
|
+
pad=$((line_width - ${#header_left} - ${#header_right}))
|
|
353
|
+
[ "$pad" -lt 1 ] && pad=1
|
|
354
|
+
printf "%s%*s%s\n" "$header_left" "$pad" "" "$header_right"
|
|
355
|
+
|
|
356
|
+
sdelta=$(_pct_delta "$sw" "$sp")
|
|
357
|
+
sann=""
|
|
358
|
+
[ -n "$sdelta" ] && sann="${sdelta} vs last week"
|
|
359
|
+
cann=""
|
|
360
|
+
if [ "$ca" -gt 0 ] 2>/dev/null; then
|
|
361
|
+
if [ "$ca" -eq 1 ] 2>/dev/null; then cann="1 author"; else cann="${ca} authors"; fi
|
|
362
|
+
fi
|
|
363
|
+
hann=""
|
|
364
|
+
if [ -n "$havg" ]; then hann="$(_num_float "$havg")d avg resolution"; fi
|
|
365
|
+
kann="$(_commafy "$kall") artifacts total"
|
|
366
|
+
know_week="+${kw}"
|
|
367
|
+
|
|
368
|
+
printf " %-11s%5s 5-week %s %s\n\n" "sessions" "$sw" "$(_spark "$sb")" "$sann"
|
|
369
|
+
printf " %-11s%5s 5-week %s %s\n\n" "commits" "$cw" "$(_spark "$cb")" "$cann"
|
|
370
|
+
printf " %-11s%5s 5-week %s %s\n\n" "handoffs" "$hw" "$(_spark "$hb")" "$hann"
|
|
371
|
+
printf " %-11s%5s 5-week %s %s\n" "knowledge" "$know_week" "$(_spark "$kb")" "$kann"
|
|
372
|
+
echo "$separator"
|
|
373
|
+
printf " %s sessions · %s commits · %s handoffs all-time\n" "$(_commafy "$sall")" "$(_commafy "$call")" "$(_commafy "$hall")"
|
|
374
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# noncode.sh — shared "non-coding change" classifier for auto-merge decisions.
|
|
3
|
+
#
|
|
4
|
+
# The org rule: non-coding PRs auto-merge to develop; code goes to review.
|
|
5
|
+
# Non-coding means:
|
|
6
|
+
# - any .md file, anywhere
|
|
7
|
+
# - anything under content directories (artifacts/, docs/, .threads/) —
|
|
8
|
+
# html/css/images/design bundles there are authored content, not shipped code
|
|
9
|
+
# - session-state droppings (.egregore-worktree-tty)
|
|
10
|
+
# Everything else — bin/, api/, packages/, .claude/, site 2/, root config — is
|
|
11
|
+
# code and stays open for review.
|
|
12
|
+
#
|
|
13
|
+
# Sourced by handoff-save-egregore.sh, session-autosave.sh, agent.sh, and the
|
|
14
|
+
# /save skill. Keep the policy HERE — call sites must not grow their own.
|
|
15
|
+
|
|
16
|
+
# noncode_filter — reads paths on stdin, prints the first code-bearing path.
|
|
17
|
+
# Empty output = everything is non-coding.
|
|
18
|
+
noncode_filter() {
|
|
19
|
+
grep -Ev '\.md$' \
|
|
20
|
+
| grep -Ev '^(artifacts|docs|\.threads)/' \
|
|
21
|
+
| grep -Ev '^\.egregore-worktree-tty$' \
|
|
22
|
+
| head -1 || true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# noncode_blockers [base-ref] [repo-dir] — first code-bearing path committed
|
|
26
|
+
# on HEAD relative to base (three-dot: only our side of the diff).
|
|
27
|
+
# Empty output = the branch is non-coding and safe to auto-merge.
|
|
28
|
+
noncode_blockers() {
|
|
29
|
+
local base="${1:-origin/develop}" dir="${2:-.}"
|
|
30
|
+
git -C "$dir" diff "$base...HEAD" --name-only 2>/dev/null | noncode_filter
|
|
31
|
+
}
|