create-egregore 0.21.6 → 0.22.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/assets/egregore-launcher.js +58 -6
- package/assets/egregore-launcher.sh +15 -2
- package/lib/claude-runtime.js +2 -0
- package/lib/codex-runtime.js +2 -0
- package/lib/setup.js +20 -3
- package/package.json +2 -2
- package/runtime/claude/.claude/context/commit-format.md +7 -4
- package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
- package/runtime/claude/.claude/context/notification-consent.md +12 -9
- package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
- package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
- package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
- package/runtime/claude/.claude/settings.json +1 -1
- package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
- package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
- package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
- package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
- package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
- package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
- package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
- package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
- package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
- package/runtime/claude/CLAUDE.md +59 -95
- package/runtime/claude/bin/agent.sh +231 -7
- package/runtime/claude/bin/api-call.sh +44 -23
- package/runtime/claude/bin/artifact-register.sh +7 -1
- package/runtime/claude/bin/artifact-writeback.sh +1 -1
- package/runtime/claude/bin/artifacts.sh +66 -265
- package/runtime/claude/bin/attendant.sh +4 -1
- package/runtime/claude/bin/boundary-guard.cjs +1848 -0
- package/runtime/claude/bin/boundary-hook.sh +41 -0
- package/runtime/claude/bin/boundary.sh +15 -155
- package/runtime/claude/bin/capture-run.sh +32 -2
- package/runtime/claude/bin/codex-render-spec.mjs +24 -46
- package/runtime/claude/bin/codex-session-start.sh +19 -0
- package/runtime/claude/bin/codex-skill-render.mjs +18 -0
- package/runtime/claude/bin/config-get.sh +10 -1
- package/runtime/claude/bin/connect-refresh.sh +16 -0
- package/runtime/claude/bin/corpus_map.py +5 -0
- package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
- package/runtime/claude/bin/genui.sh +19 -0
- package/runtime/claude/bin/graph-batch.sh +1 -1
- package/runtime/claude/bin/graph-op.sh +14 -0
- package/runtime/claude/bin/graph.sh +1 -1
- package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/claude/bin/handoff-run.sh +11 -6
- package/runtime/claude/bin/ingest-item.sh +1 -1
- package/runtime/claude/bin/ingest-surface.html +22 -4
- package/runtime/claude/bin/issue.sh +1 -1
- package/runtime/claude/bin/knowledge.sh +1 -1
- package/runtime/claude/bin/lib/card-delivery.sh +329 -0
- package/runtime/claude/bin/lib/config.sh +148 -3
- package/runtime/claude/bin/lib/context.sh +38 -67
- package/runtime/claude/bin/lib/genui-entry.py +36 -0
- package/runtime/claude/bin/lib/git-message.sh +25 -6
- package/runtime/claude/bin/lib/git-sync.sh +12 -4
- package/runtime/claude/bin/lib/greeting.sh +255 -28
- package/runtime/claude/bin/lib/handoff_meta.py +294 -0
- package/runtime/claude/bin/lib/health-footer.sh +83 -2
- package/runtime/claude/bin/lib/identity.sh +31 -10
- package/runtime/claude/bin/lib/instance-registry.sh +71 -0
- package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/claude/bin/lib/metrics.sh +121 -8
- package/runtime/claude/bin/lib/notices.sh +36 -0
- package/runtime/claude/bin/lib/record_outcomes.py +729 -0
- package/runtime/claude/bin/lib/reflexes.py +267 -0
- package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/claude/bin/lib/session_record.py +3747 -0
- package/runtime/claude/bin/lib/session_trail.py +1983 -0
- package/runtime/claude/bin/lib/settings-drift.sh +8 -5
- package/runtime/claude/bin/lib/startup-restart.sh +41 -0
- package/runtime/claude/bin/lib/worktree-links.sh +92 -0
- package/runtime/claude/bin/memory-push.sh +6 -0
- package/runtime/claude/bin/merge-gate.sh +498 -0
- package/runtime/claude/bin/notify.sh +5 -2
- package/runtime/claude/bin/page-access.sh +103 -0
- package/runtime/claude/bin/person.sh +13 -2
- package/runtime/claude/bin/pi-render-spec.mjs +0 -1
- package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/claude/bin/post-update.sh +181 -0
- package/runtime/claude/bin/prime-render-spec.mjs +0 -1
- package/runtime/claude/bin/publish-artifact.sh +89 -42
- package/runtime/claude/bin/qa-inventory.sh +2 -2
- package/runtime/claude/bin/quest.sh +1 -1
- package/runtime/claude/bin/records.sh +11 -0
- package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/claude/bin/reflexes/30-cost.py +116 -0
- package/runtime/claude/bin/reflexes/40-shells.py +51 -0
- package/runtime/claude/bin/reflexes/README.md +61 -0
- package/runtime/claude/bin/reflexes.sh +7 -0
- package/runtime/claude/bin/runtime-identity.sh +140 -42
- package/runtime/claude/bin/screenshot.sh +77 -0
- package/runtime/claude/bin/scroll.sh +1 -1
- package/runtime/claude/bin/search.sh +31 -5
- package/runtime/claude/bin/session-autosave.sh +6 -3
- package/runtime/claude/bin/session-id.sh +147 -0
- package/runtime/claude/bin/session-log.sh +19 -0
- package/runtime/claude/bin/session-record.sh +26 -0
- package/runtime/claude/bin/session-start.sh +121 -151
- package/runtime/claude/bin/settings.sh +13 -8
- package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/claude/bin/spirit-discovery.sh +4 -0
- package/runtime/claude/bin/startup-check.sh +1 -1
- package/runtime/claude/bin/telemetry.sh +11 -4
- package/runtime/claude/bin/test-baseline.sh +33 -5
- package/runtime/claude/bin/todo.sh +1 -1
- package/runtime/claude/bin/worktree-create.sh +15 -45
- package/runtime/claude/bin/worktree.sh +6 -0
- package/runtime/claude/docs/spirit-discovery.md +96 -0
- package/runtime/claude/egregore_runtime/__init__.py +18 -12
- package/runtime/claude/egregore_runtime/access.py +608 -0
- package/runtime/claude/egregore_runtime/access_cli.py +190 -0
- package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
- package/runtime/claude/egregore_runtime/artifacts.py +222 -16
- package/runtime/claude/egregore_runtime/contracts.py +27 -0
- package/runtime/claude/egregore_runtime/eligibility.py +5 -12
- package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
- package/runtime/claude/egregore_runtime/github_issues.py +2 -2
- package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
- package/runtime/claude/egregore_runtime/held.py +2436 -0
- package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
- package/runtime/claude/egregore_runtime/identity.py +196 -65
- package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/claude/egregore_runtime/installation.py +119 -0
- package/runtime/claude/egregore_runtime/investigation.py +64 -41
- package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
- package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/claude/egregore_runtime/issues.py +12 -6
- package/runtime/claude/egregore_runtime/knowledge.py +331 -5
- package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
- package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/claude/egregore_runtime/line_ids.py +10 -0
- package/runtime/claude/egregore_runtime/memory_push.py +349 -0
- package/runtime/claude/egregore_runtime/observe.py +64 -25
- package/runtime/claude/egregore_runtime/observers.py +19 -0
- package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
- package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/claude/egregore_runtime/policy.py +651 -24
- package/runtime/claude/egregore_runtime/private_result.py +2 -1
- package/runtime/claude/egregore_runtime/projection.py +946 -0
- package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
- package/runtime/claude/egregore_runtime/question_cli.py +15 -11
- package/runtime/claude/egregore_runtime/questions.py +10 -10
- package/runtime/claude/egregore_runtime/quests.py +10 -10
- package/runtime/claude/egregore_runtime/record_publish.py +548 -0
- package/runtime/claude/egregore_runtime/record_subject.py +564 -0
- package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
- package/runtime/claude/egregore_runtime/runtime.py +169 -60
- package/runtime/claude/egregore_runtime/sandbox.py +194 -0
- package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/claude/egregore_runtime/scrolls.py +23 -15
- package/runtime/claude/egregore_runtime/shadow.py +262 -0
- package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/claude/egregore_runtime/status.py +101 -44
- package/runtime/claude/egregore_runtime/status_cli.py +13 -5
- package/runtime/claude/egregore_runtime/sync.py +39 -21
- package/runtime/claude/egregore_runtime/taste.py +934 -0
- package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
- package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
- package/runtime/claude/egregore_runtime/telemetry.py +26 -3
- package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
- package/runtime/claude/egregore_runtime/todos.py +3 -2
- package/runtime/claude/egregore_runtime/upgrade.py +85 -58
- package/runtime/claude/egregore_runtime/writeback.py +330 -24
- package/runtime/claude/manifest.json +65 -5
- package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
- package/runtime/codex/.claude/context/notification-consent.md +12 -9
- package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
- package/runtime/codex/.codex/hooks.json +11 -0
- package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/codex/.codex/spec-manifest.json +13 -19
- package/runtime/codex/AGENTS.md +54 -105
- package/runtime/codex/bin/agent.sh +231 -7
- package/runtime/codex/bin/api-call.sh +44 -23
- package/runtime/codex/bin/artifact-register.sh +7 -1
- package/runtime/codex/bin/artifact-writeback.sh +1 -1
- package/runtime/codex/bin/artifacts.sh +66 -265
- package/runtime/codex/bin/attendant.sh +4 -1
- package/runtime/codex/bin/boundary-guard.cjs +1848 -0
- package/runtime/codex/bin/boundary-hook.sh +41 -0
- package/runtime/codex/bin/capture-run.sh +32 -2
- package/runtime/codex/bin/codex-render-spec.mjs +24 -46
- package/runtime/codex/bin/codex-session-start.sh +19 -0
- package/runtime/codex/bin/codex-skill-render.mjs +18 -0
- package/runtime/codex/bin/config-get.sh +10 -1
- package/runtime/codex/bin/connect-refresh.sh +16 -0
- package/runtime/codex/bin/corpus_map.py +5 -0
- package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
- package/runtime/codex/bin/genui.sh +19 -0
- package/runtime/codex/bin/graph-batch.sh +1 -1
- package/runtime/codex/bin/graph-op.sh +14 -0
- package/runtime/codex/bin/graph.sh +1 -1
- package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/codex/bin/handoff-run.sh +11 -6
- package/runtime/codex/bin/ingest-item.sh +1 -1
- package/runtime/codex/bin/ingest-surface.html +22 -4
- package/runtime/codex/bin/issue.sh +1 -1
- package/runtime/codex/bin/knowledge.sh +1 -1
- package/runtime/codex/bin/lib/card-delivery.sh +329 -0
- package/runtime/codex/bin/lib/config.sh +148 -3
- package/runtime/codex/bin/lib/context.sh +38 -67
- package/runtime/codex/bin/lib/genui-entry.py +36 -0
- package/runtime/codex/bin/lib/git-message.sh +25 -6
- package/runtime/codex/bin/lib/git-sync.sh +12 -4
- package/runtime/codex/bin/lib/greeting.sh +255 -28
- package/runtime/codex/bin/lib/handoff_meta.py +294 -0
- package/runtime/codex/bin/lib/health-footer.sh +83 -2
- package/runtime/codex/bin/lib/identity.sh +31 -10
- package/runtime/codex/bin/lib/instance-registry.sh +71 -0
- package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/codex/bin/lib/metrics.sh +121 -8
- package/runtime/codex/bin/lib/notices.sh +36 -0
- package/runtime/codex/bin/lib/record_outcomes.py +729 -0
- package/runtime/codex/bin/lib/reflexes.py +267 -0
- package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/codex/bin/lib/session_record.py +3747 -0
- package/runtime/codex/bin/lib/session_trail.py +1983 -0
- package/runtime/codex/bin/lib/settings-drift.sh +8 -5
- package/runtime/codex/bin/lib/startup-restart.sh +41 -0
- package/runtime/codex/bin/lib/worktree-links.sh +92 -0
- package/runtime/codex/bin/memory-push.sh +6 -0
- package/runtime/codex/bin/merge-gate.sh +498 -0
- package/runtime/codex/bin/notify.sh +5 -2
- package/runtime/codex/bin/page-access.sh +103 -0
- package/runtime/codex/bin/person.sh +13 -2
- package/runtime/codex/bin/pi-render-spec.mjs +0 -1
- package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/codex/bin/post-update.sh +181 -0
- package/runtime/codex/bin/prime-render-spec.mjs +0 -1
- package/runtime/codex/bin/publish-artifact.sh +89 -42
- package/runtime/codex/bin/qa-inventory.sh +2 -2
- package/runtime/codex/bin/quest.sh +1 -1
- package/runtime/codex/bin/records.sh +11 -0
- package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/codex/bin/reflexes/30-cost.py +116 -0
- package/runtime/codex/bin/reflexes/40-shells.py +51 -0
- package/runtime/codex/bin/reflexes/README.md +61 -0
- package/runtime/codex/bin/reflexes.sh +7 -0
- package/runtime/codex/bin/runtime-identity.sh +140 -42
- package/runtime/codex/bin/screenshot.sh +77 -0
- package/runtime/codex/bin/scroll.sh +1 -1
- package/runtime/codex/bin/search.sh +31 -5
- package/runtime/codex/bin/session-autosave.sh +6 -3
- package/runtime/codex/bin/session-id.sh +147 -0
- package/runtime/codex/bin/session-log.sh +19 -0
- package/runtime/codex/bin/session-record.sh +26 -0
- package/runtime/codex/bin/settings.sh +13 -8
- package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/codex/bin/spirit-discovery.sh +4 -0
- package/runtime/codex/bin/startup-check.sh +1 -1
- package/runtime/codex/bin/telemetry.sh +11 -4
- package/runtime/codex/bin/test-baseline.sh +33 -5
- package/runtime/codex/bin/todo.sh +1 -1
- package/runtime/codex/bin/worktree-create.sh +15 -45
- package/runtime/codex/bin/worktree.sh +6 -0
- package/runtime/codex/docs/spirit-discovery.md +96 -0
- package/runtime/codex/egregore_runtime/__init__.py +18 -12
- package/runtime/codex/egregore_runtime/access.py +608 -0
- package/runtime/codex/egregore_runtime/access_cli.py +190 -0
- package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
- package/runtime/codex/egregore_runtime/artifacts.py +222 -16
- package/runtime/codex/egregore_runtime/contracts.py +27 -0
- package/runtime/codex/egregore_runtime/eligibility.py +5 -12
- package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
- package/runtime/codex/egregore_runtime/github_issues.py +2 -2
- package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
- package/runtime/codex/egregore_runtime/held.py +2436 -0
- package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
- package/runtime/codex/egregore_runtime/identity.py +196 -65
- package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/codex/egregore_runtime/installation.py +119 -0
- package/runtime/codex/egregore_runtime/investigation.py +64 -41
- package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
- package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/codex/egregore_runtime/issues.py +12 -6
- package/runtime/codex/egregore_runtime/knowledge.py +331 -5
- package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
- package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/codex/egregore_runtime/line_ids.py +10 -0
- package/runtime/codex/egregore_runtime/memory_push.py +349 -0
- package/runtime/codex/egregore_runtime/observe.py +64 -25
- package/runtime/codex/egregore_runtime/observers.py +19 -0
- package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
- package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/codex/egregore_runtime/policy.py +651 -24
- package/runtime/codex/egregore_runtime/private_result.py +2 -1
- package/runtime/codex/egregore_runtime/projection.py +946 -0
- package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
- package/runtime/codex/egregore_runtime/question_cli.py +15 -11
- package/runtime/codex/egregore_runtime/questions.py +10 -10
- package/runtime/codex/egregore_runtime/quests.py +10 -10
- package/runtime/codex/egregore_runtime/record_publish.py +548 -0
- package/runtime/codex/egregore_runtime/record_subject.py +564 -0
- package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
- package/runtime/codex/egregore_runtime/runtime.py +169 -60
- package/runtime/codex/egregore_runtime/sandbox.py +194 -0
- package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/codex/egregore_runtime/scrolls.py +23 -15
- package/runtime/codex/egregore_runtime/shadow.py +262 -0
- package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/codex/egregore_runtime/status.py +101 -44
- package/runtime/codex/egregore_runtime/status_cli.py +13 -5
- package/runtime/codex/egregore_runtime/sync.py +39 -21
- package/runtime/codex/egregore_runtime/taste.py +934 -0
- package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
- package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
- package/runtime/codex/egregore_runtime/telemetry.py +26 -3
- package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
- package/runtime/codex/egregore_runtime/todos.py +3 -2
- package/runtime/codex/egregore_runtime/upgrade.py +85 -58
- package/runtime/codex/egregore_runtime/writeback.py +330 -24
- package/runtime/codex/manifest.json +66 -5
- package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
- package/runtime/pi/.claude/context/notification-consent.md +12 -9
- package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
- package/runtime/pi/.codex/hooks.json +11 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/pi/.codex/spec-manifest.json +13 -19
- package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
- package/runtime/pi/.pi/extensions/egregore.ts +13 -1
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +54 -105
- package/runtime/pi/bin/agent.sh +231 -7
- package/runtime/pi/bin/api-call.sh +44 -23
- package/runtime/pi/bin/artifact-register.sh +7 -1
- package/runtime/pi/bin/artifact-writeback.sh +1 -1
- package/runtime/pi/bin/artifacts.sh +66 -265
- package/runtime/pi/bin/attendant.sh +4 -1
- package/runtime/pi/bin/boundary-guard.cjs +1848 -0
- package/runtime/pi/bin/boundary-hook.sh +41 -0
- package/runtime/pi/bin/capture-run.sh +32 -2
- package/runtime/pi/bin/codex-render-spec.mjs +24 -46
- package/runtime/pi/bin/codex-session-start.sh +19 -0
- package/runtime/pi/bin/codex-skill-render.mjs +18 -0
- package/runtime/pi/bin/config-get.sh +10 -1
- package/runtime/pi/bin/connect-refresh.sh +16 -0
- package/runtime/pi/bin/corpus_map.py +5 -0
- package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
- package/runtime/pi/bin/genui.sh +19 -0
- package/runtime/pi/bin/graph-batch.sh +1 -1
- package/runtime/pi/bin/graph-op.sh +14 -0
- package/runtime/pi/bin/graph.sh +1 -1
- package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/pi/bin/handoff-run.sh +11 -6
- package/runtime/pi/bin/ingest-item.sh +1 -1
- package/runtime/pi/bin/ingest-surface.html +22 -4
- package/runtime/pi/bin/issue.sh +1 -1
- package/runtime/pi/bin/knowledge.sh +1 -1
- package/runtime/pi/bin/lib/card-delivery.sh +329 -0
- package/runtime/pi/bin/lib/config.sh +148 -3
- package/runtime/pi/bin/lib/context.sh +38 -67
- package/runtime/pi/bin/lib/genui-entry.py +36 -0
- package/runtime/pi/bin/lib/git-message.sh +25 -6
- package/runtime/pi/bin/lib/git-sync.sh +12 -4
- package/runtime/pi/bin/lib/greeting.sh +255 -28
- package/runtime/pi/bin/lib/handoff_meta.py +294 -0
- package/runtime/pi/bin/lib/health-footer.sh +83 -2
- package/runtime/pi/bin/lib/identity.sh +31 -10
- package/runtime/pi/bin/lib/instance-registry.sh +71 -0
- package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/pi/bin/lib/metrics.sh +121 -8
- package/runtime/pi/bin/lib/notices.sh +36 -0
- package/runtime/pi/bin/lib/record_outcomes.py +729 -0
- package/runtime/pi/bin/lib/reflexes.py +267 -0
- package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/pi/bin/lib/session_record.py +3747 -0
- package/runtime/pi/bin/lib/session_trail.py +1983 -0
- package/runtime/pi/bin/lib/settings-drift.sh +8 -5
- package/runtime/pi/bin/lib/startup-restart.sh +41 -0
- package/runtime/pi/bin/lib/worktree-links.sh +92 -0
- package/runtime/pi/bin/memory-push.sh +6 -0
- package/runtime/pi/bin/merge-gate.sh +498 -0
- package/runtime/pi/bin/notify.sh +5 -2
- package/runtime/pi/bin/page-access.sh +103 -0
- package/runtime/pi/bin/person.sh +13 -2
- package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
- package/runtime/pi/bin/pi-observe-context.mjs +97 -4
- package/runtime/pi/bin/pi-render-spec.mjs +0 -1
- package/runtime/pi/bin/pi-session-start.sh +4 -1
- package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/pi/bin/post-update.sh +181 -0
- package/runtime/pi/bin/prime-render-spec.mjs +0 -1
- package/runtime/pi/bin/publish-artifact.sh +89 -42
- package/runtime/pi/bin/qa-inventory.sh +2 -2
- package/runtime/pi/bin/quest.sh +1 -1
- package/runtime/pi/bin/records.sh +11 -0
- package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/pi/bin/reflexes/30-cost.py +116 -0
- package/runtime/pi/bin/reflexes/40-shells.py +51 -0
- package/runtime/pi/bin/reflexes/README.md +61 -0
- package/runtime/pi/bin/reflexes.sh +7 -0
- package/runtime/pi/bin/runtime-identity.sh +140 -42
- package/runtime/pi/bin/screenshot.sh +77 -0
- package/runtime/pi/bin/scroll.sh +1 -1
- package/runtime/pi/bin/search.sh +31 -5
- package/runtime/pi/bin/session-autosave.sh +6 -3
- package/runtime/pi/bin/session-id.sh +147 -0
- package/runtime/pi/bin/session-log.sh +19 -0
- package/runtime/pi/bin/session-record.sh +26 -0
- package/runtime/pi/bin/settings.sh +13 -8
- package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/pi/bin/spirit-discovery.sh +4 -0
- package/runtime/pi/bin/startup-check.sh +1 -1
- package/runtime/pi/bin/telemetry.sh +11 -4
- package/runtime/pi/bin/test-baseline.sh +33 -5
- package/runtime/pi/bin/todo.sh +1 -1
- package/runtime/pi/bin/worktree-create.sh +15 -45
- package/runtime/pi/bin/worktree.sh +6 -0
- package/runtime/pi/docs/spirit-discovery.md +96 -0
- package/runtime/pi/egregore_runtime/__init__.py +18 -12
- package/runtime/pi/egregore_runtime/access.py +608 -0
- package/runtime/pi/egregore_runtime/access_cli.py +190 -0
- package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
- package/runtime/pi/egregore_runtime/artifacts.py +222 -16
- package/runtime/pi/egregore_runtime/contracts.py +27 -0
- package/runtime/pi/egregore_runtime/eligibility.py +5 -12
- package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
- package/runtime/pi/egregore_runtime/github_issues.py +2 -2
- package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
- package/runtime/pi/egregore_runtime/held.py +2436 -0
- package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
- package/runtime/pi/egregore_runtime/identity.py +196 -65
- package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/pi/egregore_runtime/installation.py +119 -0
- package/runtime/pi/egregore_runtime/investigation.py +64 -41
- package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
- package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/pi/egregore_runtime/issues.py +12 -6
- package/runtime/pi/egregore_runtime/knowledge.py +331 -5
- package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
- package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/pi/egregore_runtime/line_ids.py +10 -0
- package/runtime/pi/egregore_runtime/memory_push.py +349 -0
- package/runtime/pi/egregore_runtime/observe.py +64 -25
- package/runtime/pi/egregore_runtime/observers.py +19 -0
- package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
- package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/pi/egregore_runtime/policy.py +651 -24
- package/runtime/pi/egregore_runtime/private_result.py +2 -1
- package/runtime/pi/egregore_runtime/projection.py +946 -0
- package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
- package/runtime/pi/egregore_runtime/question_cli.py +15 -11
- package/runtime/pi/egregore_runtime/questions.py +10 -10
- package/runtime/pi/egregore_runtime/quests.py +10 -10
- package/runtime/pi/egregore_runtime/record_publish.py +548 -0
- package/runtime/pi/egregore_runtime/record_subject.py +564 -0
- package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
- package/runtime/pi/egregore_runtime/runtime.py +169 -60
- package/runtime/pi/egregore_runtime/sandbox.py +194 -0
- package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/pi/egregore_runtime/scrolls.py +23 -15
- package/runtime/pi/egregore_runtime/shadow.py +262 -0
- package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/pi/egregore_runtime/status.py +101 -44
- package/runtime/pi/egregore_runtime/status_cli.py +13 -5
- package/runtime/pi/egregore_runtime/sync.py +39 -21
- package/runtime/pi/egregore_runtime/taste.py +934 -0
- package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
- package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
- package/runtime/pi/egregore_runtime/telemetry.py +26 -3
- package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
- package/runtime/pi/egregore_runtime/todos.py +3 -2
- package/runtime/pi/egregore_runtime/upgrade.py +85 -58
- package/runtime/pi/egregore_runtime/writeback.py +330 -24
- package/runtime/pi/manifest.json +66 -5
- package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
- package/runtime/prime/.claude/context/notification-consent.md +12 -9
- package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
- package/runtime/prime/.codex/hooks.json +11 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/prime/.codex/spec-manifest.json +13 -19
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
- package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
- package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
- package/runtime/prime/AGENTS.md +54 -105
- package/runtime/prime/bin/agent.sh +231 -7
- package/runtime/prime/bin/api-call.sh +44 -23
- package/runtime/prime/bin/artifact-register.sh +7 -1
- package/runtime/prime/bin/artifact-writeback.sh +1 -1
- package/runtime/prime/bin/artifacts.sh +66 -265
- package/runtime/prime/bin/attendant.sh +4 -1
- package/runtime/prime/bin/boundary-guard.cjs +1848 -0
- package/runtime/prime/bin/boundary-hook.sh +41 -0
- package/runtime/prime/bin/capture-run.sh +32 -2
- package/runtime/prime/bin/codex-render-spec.mjs +24 -46
- package/runtime/prime/bin/codex-session-start.sh +19 -0
- package/runtime/prime/bin/codex-skill-render.mjs +18 -0
- package/runtime/prime/bin/config-get.sh +10 -1
- package/runtime/prime/bin/connect-refresh.sh +16 -0
- package/runtime/prime/bin/corpus_map.py +5 -0
- package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
- package/runtime/prime/bin/genui.sh +19 -0
- package/runtime/prime/bin/graph-batch.sh +1 -1
- package/runtime/prime/bin/graph-op.sh +14 -0
- package/runtime/prime/bin/graph.sh +1 -1
- package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/prime/bin/handoff-run.sh +11 -6
- package/runtime/prime/bin/ingest-item.sh +1 -1
- package/runtime/prime/bin/ingest-surface.html +22 -4
- package/runtime/prime/bin/issue.sh +1 -1
- package/runtime/prime/bin/knowledge.sh +1 -1
- package/runtime/prime/bin/lib/card-delivery.sh +329 -0
- package/runtime/prime/bin/lib/config.sh +148 -3
- package/runtime/prime/bin/lib/context.sh +38 -67
- package/runtime/prime/bin/lib/genui-entry.py +36 -0
- package/runtime/prime/bin/lib/git-message.sh +25 -6
- package/runtime/prime/bin/lib/git-sync.sh +12 -4
- package/runtime/prime/bin/lib/greeting.sh +255 -28
- package/runtime/prime/bin/lib/handoff_meta.py +294 -0
- package/runtime/prime/bin/lib/health-footer.sh +83 -2
- package/runtime/prime/bin/lib/identity.sh +31 -10
- package/runtime/prime/bin/lib/instance-registry.sh +71 -0
- package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/prime/bin/lib/metrics.sh +121 -8
- package/runtime/prime/bin/lib/notices.sh +36 -0
- package/runtime/prime/bin/lib/record_outcomes.py +729 -0
- package/runtime/prime/bin/lib/reflexes.py +267 -0
- package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/prime/bin/lib/session_record.py +3747 -0
- package/runtime/prime/bin/lib/session_trail.py +1983 -0
- package/runtime/prime/bin/lib/settings-drift.sh +8 -5
- package/runtime/prime/bin/lib/startup-restart.sh +41 -0
- package/runtime/prime/bin/lib/worktree-links.sh +92 -0
- package/runtime/prime/bin/memory-push.sh +6 -0
- package/runtime/prime/bin/merge-gate.sh +498 -0
- package/runtime/prime/bin/notify.sh +5 -2
- package/runtime/prime/bin/page-access.sh +103 -0
- package/runtime/prime/bin/person.sh +13 -2
- package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
- package/runtime/prime/bin/pi-observe-context.mjs +97 -4
- package/runtime/prime/bin/pi-render-spec.mjs +0 -1
- package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/prime/bin/post-update.sh +181 -0
- package/runtime/prime/bin/prime-render-spec.mjs +0 -1
- package/runtime/prime/bin/prime-session-start.sh +3 -1
- package/runtime/prime/bin/publish-artifact.sh +89 -42
- package/runtime/prime/bin/qa-inventory.sh +2 -2
- package/runtime/prime/bin/quest.sh +1 -1
- package/runtime/prime/bin/records.sh +11 -0
- package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/prime/bin/reflexes/30-cost.py +116 -0
- package/runtime/prime/bin/reflexes/40-shells.py +51 -0
- package/runtime/prime/bin/reflexes/README.md +61 -0
- package/runtime/prime/bin/reflexes.sh +7 -0
- package/runtime/prime/bin/runtime-identity.sh +140 -42
- package/runtime/prime/bin/screenshot.sh +77 -0
- package/runtime/prime/bin/scroll.sh +1 -1
- package/runtime/prime/bin/search.sh +31 -5
- package/runtime/prime/bin/session-autosave.sh +6 -3
- package/runtime/prime/bin/session-id.sh +147 -0
- package/runtime/prime/bin/session-log.sh +19 -0
- package/runtime/prime/bin/session-record.sh +26 -0
- package/runtime/prime/bin/settings.sh +13 -8
- package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/prime/bin/spirit-discovery.sh +4 -0
- package/runtime/prime/bin/startup-check.sh +1 -1
- package/runtime/prime/bin/telemetry.sh +11 -4
- package/runtime/prime/bin/test-baseline.sh +33 -5
- package/runtime/prime/bin/todo.sh +1 -1
- package/runtime/prime/bin/worktree-create.sh +15 -45
- package/runtime/prime/bin/worktree.sh +6 -0
- package/runtime/prime/docs/spirit-discovery.md +96 -0
- package/runtime/prime/egregore_runtime/__init__.py +18 -12
- package/runtime/prime/egregore_runtime/access.py +608 -0
- package/runtime/prime/egregore_runtime/access_cli.py +190 -0
- package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
- package/runtime/prime/egregore_runtime/artifacts.py +222 -16
- package/runtime/prime/egregore_runtime/contracts.py +27 -0
- package/runtime/prime/egregore_runtime/eligibility.py +5 -12
- package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
- package/runtime/prime/egregore_runtime/github_issues.py +2 -2
- package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
- package/runtime/prime/egregore_runtime/held.py +2436 -0
- package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
- package/runtime/prime/egregore_runtime/identity.py +196 -65
- package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/prime/egregore_runtime/installation.py +119 -0
- package/runtime/prime/egregore_runtime/investigation.py +64 -41
- package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
- package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/prime/egregore_runtime/issues.py +12 -6
- package/runtime/prime/egregore_runtime/knowledge.py +331 -5
- package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
- package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/prime/egregore_runtime/line_ids.py +10 -0
- package/runtime/prime/egregore_runtime/memory_push.py +349 -0
- package/runtime/prime/egregore_runtime/observe.py +64 -25
- package/runtime/prime/egregore_runtime/observers.py +19 -0
- package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
- package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/prime/egregore_runtime/policy.py +651 -24
- package/runtime/prime/egregore_runtime/private_result.py +2 -1
- package/runtime/prime/egregore_runtime/projection.py +946 -0
- package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
- package/runtime/prime/egregore_runtime/question_cli.py +15 -11
- package/runtime/prime/egregore_runtime/questions.py +10 -10
- package/runtime/prime/egregore_runtime/quests.py +10 -10
- package/runtime/prime/egregore_runtime/record_publish.py +548 -0
- package/runtime/prime/egregore_runtime/record_subject.py +564 -0
- package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
- package/runtime/prime/egregore_runtime/runtime.py +169 -60
- package/runtime/prime/egregore_runtime/sandbox.py +194 -0
- package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/prime/egregore_runtime/scrolls.py +23 -15
- package/runtime/prime/egregore_runtime/shadow.py +262 -0
- package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/prime/egregore_runtime/status.py +101 -44
- package/runtime/prime/egregore_runtime/status_cli.py +13 -5
- package/runtime/prime/egregore_runtime/sync.py +39 -21
- package/runtime/prime/egregore_runtime/taste.py +934 -0
- package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
- package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
- package/runtime/prime/egregore_runtime/telemetry.py +26 -3
- package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
- package/runtime/prime/egregore_runtime/todos.py +3 -2
- package/runtime/prime/egregore_runtime/upgrade.py +85 -58
- package/runtime/prime/egregore_runtime/writeback.py +330 -24
- package/runtime/prime/manifest.json +66 -5
- package/runtime/claude/bin/thread.sh +0 -9
- package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
- package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
- package/runtime/claude/egregore_runtime/threads.py +0 -521
- package/runtime/codex/bin/thread.sh +0 -9
- package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
- package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
- package/runtime/codex/egregore_runtime/threads.py +0 -521
- package/runtime/pi/bin/thread.sh +0 -9
- package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
- package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
- package/runtime/pi/egregore_runtime/threads.py +0 -521
- package/runtime/prime/bin/thread.sh +0 -9
- package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
- package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
- package/runtime/prime/egregore_runtime/threads.py +0 -521
|
@@ -18,6 +18,205 @@
|
|
|
18
18
|
# HEALTH_*, FRAMEWORK_VERSION, TIME_OF_DAY, EGREGORE_SESSION_ID,
|
|
19
19
|
# FIRST_SESSION, DISPLAY_NAME_STATE, DASHBOARD_URL
|
|
20
20
|
|
|
21
|
+
# Session records reuse the index refreshed by the detached sweep.
|
|
22
|
+
# No transcript is opened by a greeting.
|
|
23
|
+
# Personal controls always win over the organization default.
|
|
24
|
+
_print_session_records() {
|
|
25
|
+
[ -n "${HOME:-}" ] || return 0
|
|
26
|
+
command -v python3 >/dev/null 2>&1 || return 0
|
|
27
|
+
python3 - "${CONFIG:-$SCRIPT_DIR/egregore.json}" "$STATE_FILE" "$SCRIPT_DIR" \
|
|
28
|
+
"${EGREGORE_MEMORY_ROOT:-$SCRIPT_DIR/memory}" "${AUTHOR:-}" <<'RECORDS_PY' 2>/dev/null || true
|
|
29
|
+
import datetime as dt
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
import re
|
|
34
|
+
import stat
|
|
35
|
+
import sys
|
|
36
|
+
|
|
37
|
+
config_path, state_path, project_root, memory_root, fallback_author = sys.argv[1:]
|
|
38
|
+
def read_json(path):
|
|
39
|
+
try:
|
|
40
|
+
value = json.loads(Path(path).read_text())
|
|
41
|
+
return value if isinstance(value, dict) else {}
|
|
42
|
+
except (OSError, ValueError):
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
config = read_json(config_path)
|
|
46
|
+
policy = config.get("features", {}).get("session_records", {})
|
|
47
|
+
if not isinstance(policy, dict) or policy.get("enabled") is not True:
|
|
48
|
+
raise SystemExit(0)
|
|
49
|
+
state = read_json(state_path)
|
|
50
|
+
author = state.get("github_username", fallback_author)
|
|
51
|
+
sys.path.insert(0, str(Path(project_root) / "bin" / "lib"))
|
|
52
|
+
try:
|
|
53
|
+
from session_trail import instance_prefix, valid_trail_folder
|
|
54
|
+
from session_record import collect_digest, metadata, sessions_directory, classify_record, Context, native_record_reader
|
|
55
|
+
except ImportError:
|
|
56
|
+
raise SystemExit(0)
|
|
57
|
+
prefix = instance_prefix(project_root)
|
|
58
|
+
projects = Path(os.environ["HOME"]) / ".claude" / "projects"
|
|
59
|
+
trail_root = Path(os.environ["HOME"]) / ".egregore" / "trails" / prefix
|
|
60
|
+
cache = {}
|
|
61
|
+
cache_path = trail_root / "index.json"
|
|
62
|
+
try:
|
|
63
|
+
if (valid_trail_folder(trail_root, projects, prefix)
|
|
64
|
+
and stat.S_ISREG(cache_path.lstat().st_mode)
|
|
65
|
+
and cache_path.resolve().is_relative_to(trail_root.resolve())):
|
|
66
|
+
cache = read_json(cache_path)
|
|
67
|
+
except OSError:
|
|
68
|
+
pass
|
|
69
|
+
today = dt.datetime.now().astimezone().date()
|
|
70
|
+
|
|
71
|
+
def clean(value):
|
|
72
|
+
return re.sub(r"[\x00-\x1f\x7f]", " ", str(value)).strip()
|
|
73
|
+
|
|
74
|
+
def header(text, name):
|
|
75
|
+
match = re.search(r"^\*\*" + re.escape(name) + r"\*\*:\s*(.*)$", text, re.M)
|
|
76
|
+
return match.group(1).strip() if match else ""
|
|
77
|
+
|
|
78
|
+
def frontmatter(text, name):
|
|
79
|
+
match = re.search(r"^" + re.escape(name) + r":\s*(.*)$", text.partition("\n---")[0], re.M)
|
|
80
|
+
return match.group(1).strip().strip('"\'') if match else ""
|
|
81
|
+
|
|
82
|
+
def bullets(text, name):
|
|
83
|
+
match = re.search(r"^## " + re.escape(name) + r"\s*\n(.*?)(?=^## |\Z)", text, re.M | re.S)
|
|
84
|
+
return len(re.findall(r"^\s*[-*]\s+", match.group(1), re.M)) if match else 0
|
|
85
|
+
|
|
86
|
+
records = []
|
|
87
|
+
digest_records = []
|
|
88
|
+
recorded_ids = set()
|
|
89
|
+
scanned = 0
|
|
90
|
+
# Month filenames sort chronologically; a bounded newest-first scan keeps the
|
|
91
|
+
# greeting predictable even when an organization has years of sessions.
|
|
92
|
+
sessions_root = sessions_directory(memory_root)
|
|
93
|
+
try:
|
|
94
|
+
months = sorted((p for p in sessions_root.iterdir()
|
|
95
|
+
if stat.S_ISDIR(p.lstat().st_mode)
|
|
96
|
+
and p.resolve().is_relative_to(sessions_root)), reverse=True)[:120] if sessions_root else []
|
|
97
|
+
except OSError:
|
|
98
|
+
months = []
|
|
99
|
+
for month in months:
|
|
100
|
+
try:
|
|
101
|
+
paths = sorted(month.glob("*.md"), reverse=True)
|
|
102
|
+
except OSError:
|
|
103
|
+
continue
|
|
104
|
+
for path in paths[:2000 - scanned]:
|
|
105
|
+
scanned += 1
|
|
106
|
+
try:
|
|
107
|
+
if not stat.S_ISREG(path.lstat().st_mode) or not path.resolve().is_relative_to(sessions_root):
|
|
108
|
+
continue
|
|
109
|
+
with path.open() as source:
|
|
110
|
+
text = source.read(131072)
|
|
111
|
+
except (OSError, UnicodeError):
|
|
112
|
+
continue
|
|
113
|
+
if frontmatter(text, "kind") != "session-record":
|
|
114
|
+
continue
|
|
115
|
+
if state.get("session_records_noticed") is True:
|
|
116
|
+
digest_records.append((path, metadata(text), text))
|
|
117
|
+
if classify_record(metadata(text)) != "human-session":
|
|
118
|
+
continue
|
|
119
|
+
date = header(text, "Date") or frontmatter(text, "date")
|
|
120
|
+
owner = header(text, "Author") or frontmatter(text, "from")
|
|
121
|
+
session_id = header(text, "Session") or frontmatter(text, "session")
|
|
122
|
+
if session_id:
|
|
123
|
+
recorded_ids.add(session_id)
|
|
124
|
+
topic = frontmatter(text, "topic")
|
|
125
|
+
if not topic:
|
|
126
|
+
title = re.search(r"^# (?:Session: )?(.*)$", text, re.M)
|
|
127
|
+
topic = title.group(1) if title else "session"
|
|
128
|
+
commits = re.search(r"\b(\d+) commits\b", text)
|
|
129
|
+
records.append({"date": date, "author": owner, "topic": topic,
|
|
130
|
+
"duration": header(text, "Duration") or frontmatter(text, "duration") or "unknown",
|
|
131
|
+
"commits": int(commits.group(1)) if commits else 0,
|
|
132
|
+
"decisions": bullets(text, "Decided"), "open": bullets(text, "Open")})
|
|
133
|
+
if scanned >= 2000:
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
if state.get("session_records_noticed") is True:
|
|
137
|
+
# Open items are carried by the reflex line under this block
|
|
138
|
+
# (bin/reflexes/10-open-items.sh), so the digest skips the carry here.
|
|
139
|
+
has_native = any(classify_record(meta) != "human-session" for _, meta, _ in digest_records)
|
|
140
|
+
actor_id, reader = native_record_reader(Context(project_root)) if has_native else ("", None)
|
|
141
|
+
digest = collect_digest(memory_root, author=author, person=state.get("display_name", ""),
|
|
142
|
+
days=1, today=today, records=digest_records, carry_days=0,
|
|
143
|
+
actor_id=actor_id, native_reader=reader)
|
|
144
|
+
if digest["sessions"]:
|
|
145
|
+
print(f" ◦ records · yesterday {digest['sessions']} ({digest['yours']} yours) · {len(digest['decided'])} decided · {len(digest['open'])} open · /records digest")
|
|
146
|
+
def short(value, limit):
|
|
147
|
+
value = clean(value)
|
|
148
|
+
return value if len(value) <= limit else value[:limit - 1] + "…"
|
|
149
|
+
def preview(line, who):
|
|
150
|
+
suffix = " — " + short(who, 40)
|
|
151
|
+
print(" - " + short(line, 120 - 6 - len(suffix)) + suffix)
|
|
152
|
+
for item in digest["decided"][:3]:
|
|
153
|
+
preview(item["line"], item["who"])
|
|
154
|
+
if digest["spirit_intents"]:
|
|
155
|
+
noun = "intent" if digest["spirit_intents"] == 1 else "intents"
|
|
156
|
+
print(f" ◦ spirit records · {digest['spirit_intents']} {noun} ({digest['your_spirit_intents']} yours) · partial coverage · /records digest")
|
|
157
|
+
if digest["unsupported_native"]:
|
|
158
|
+
print(f" ◦ {digest['unsupported_native']} unsupported spirit records · metadata unavailable")
|
|
159
|
+
if digest["held_records"]:
|
|
160
|
+
print(f" ◦ {digest['held_records']} held records · details withheld")
|
|
161
|
+
if cache.get("waiting_push"):
|
|
162
|
+
print(" ◦ 1 records commit is waiting to push · /records status --push")
|
|
163
|
+
raise SystemExit(0)
|
|
164
|
+
|
|
165
|
+
decider = clean(policy.get("decided_by", "your organization"))
|
|
166
|
+
decided_on = clean(policy.get("decided_on", ""))
|
|
167
|
+
personal_names = {str(value).casefold() for value in (author, state.get("display_name", "")) if value}
|
|
168
|
+
if str(policy.get("decided_by", "")).casefold() in personal_names:
|
|
169
|
+
print(f"◇ Session records are on for {clean(config.get('github_org', ''))}/{clean(config.get('repo_name', 'egregore'))} · your decision, {decided_on}")
|
|
170
|
+
else:
|
|
171
|
+
print(f"◇ Session records are new · {decider} turned them on for this egregore on {decided_on}")
|
|
172
|
+
if state.get("session_records_enabled") is False:
|
|
173
|
+
print(" Your session records are off. /records on turns them back on.")
|
|
174
|
+
else:
|
|
175
|
+
print(" Each session now leaves a record in memory/sessions: asked, decided, left open,")
|
|
176
|
+
print(" touched, cost. Asks are paraphrased, never quoted. The full trail stays on this")
|
|
177
|
+
model_on = policy.get("model", "haiku") != "none" and state.get("session_records_model_call") is not False
|
|
178
|
+
if model_on:
|
|
179
|
+
print(" machine as a file; your own Claude account reads it once to write the outline,")
|
|
180
|
+
print(' through the Claude service. Say "off the record" in any session to hold it.')
|
|
181
|
+
else:
|
|
182
|
+
print(' machine. Say "off the record" in any session to hold it.')
|
|
183
|
+
print(" /records changes yours.")
|
|
184
|
+
if "unrecorded_count" in cache:
|
|
185
|
+
count = int(cache.get("unrecorded_count", 0))
|
|
186
|
+
try:
|
|
187
|
+
days = max(0, (today - dt.date.fromisoformat(cache["oldest_date"][:10])).days)
|
|
188
|
+
except (KeyError, TypeError, ValueError):
|
|
189
|
+
days = int(cache.get("days", 0))
|
|
190
|
+
if records:
|
|
191
|
+
newest = max(records, key=lambda row: row["date"])
|
|
192
|
+
if newest["author"] != author:
|
|
193
|
+
print(f" Here is one of {clean(newest['author'])}'s: {clean(newest['date'][:10])} · {clean(newest['topic'])} · {clean(newest['duration'])} · {newest['commits']} commits · {newest['decisions']} decisions · {newest['open']} open items")
|
|
194
|
+
if "unrecorded_count" in cache:
|
|
195
|
+
print(f" {count} past sessions of yours are still on this machine ({days} days). Record them?")
|
|
196
|
+
print(" y · show one · no")
|
|
197
|
+
else:
|
|
198
|
+
print(" Past sessions are being counted. The count appears at your next start.")
|
|
199
|
+
if cache.get("waiting_push"):
|
|
200
|
+
print(" ◦ 1 records commit is waiting to push · /records status --push")
|
|
201
|
+
RECORDS_PY
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# The reflexes: probes in bin/reflexes/ that each print a fact about
|
|
205
|
+
# yesterday, or nothing. One runner, fail-soft, shared by every startup card.
|
|
206
|
+
# See bin/reflexes/README.md.
|
|
207
|
+
_print_reflexes() {
|
|
208
|
+
[ -f "$SCRIPT_DIR/bin/reflexes.sh" ] || return 0
|
|
209
|
+
bash "$SCRIPT_DIR/bin/reflexes.sh" \
|
|
210
|
+
--memory "${EGREGORE_MEMORY_ROOT:-$SCRIPT_DIR/memory}" \
|
|
211
|
+
--author "${AUTHOR:-}" --harness "${EGREGORE_HARNESS:-claude}" 2>/dev/null || true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Other startup cards reuse this records block without rendering the Claude
|
|
215
|
+
# greeting or running its surrounding notices and bookkeeping.
|
|
216
|
+
if [ "${EGREGORE_GREETING_RECORDS_ONLY:-}" = "1" ]; then
|
|
217
|
+
return 0
|
|
218
|
+
fi
|
|
219
|
+
|
|
21
220
|
# --- Output greeting for Claude to display ---
|
|
22
221
|
cat << 'GREETING'
|
|
23
222
|
|
|
@@ -115,6 +314,10 @@ if [ -n "${SETTINGS_POSTURE_HELD:-}" ]; then
|
|
|
115
314
|
echo " ◇ boundary posture was set to ${SETTINGS_POSTURE_HELD} elsewhere — relaxing access needs your confirm: bash bin/settings.sh posture ${SETTINGS_POSTURE_HELD}"
|
|
116
315
|
fi
|
|
117
316
|
|
|
317
|
+
|
|
318
|
+
_print_session_records
|
|
319
|
+
_print_reflexes
|
|
320
|
+
|
|
118
321
|
# --- Footer: health + repos + memory (tertiary) ---
|
|
119
322
|
echo "$SEPARATOR"
|
|
120
323
|
|
|
@@ -126,7 +329,7 @@ _render_health_footer || true
|
|
|
126
329
|
# Runtime update summary — cached durable state only, never a live probe, so
|
|
127
330
|
# session start stays fast. One compact line; silent when nothing is staged.
|
|
128
331
|
# Keyed by stable org identity composed with the main installation path
|
|
129
|
-
# (mirrors egregore_runtime.
|
|
332
|
+
# (mirrors egregore_runtime.installation.installation_key); no org_id → no summary.
|
|
130
333
|
_UPGRADE_ORG=$(jq -r '.org_id // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
131
334
|
_UPGRADE_MAIN=$(cd "${MAIN_PROJECT_DIR:-$SCRIPT_DIR}" 2>/dev/null && pwd -P || true)
|
|
132
335
|
if [ -n "$_UPGRADE_ORG" ] && [ -n "$_UPGRADE_MAIN" ]; then
|
|
@@ -372,6 +575,43 @@ if [ "$ALIAS_VERSION" -lt 2 ] 2>/dev/null; then
|
|
|
372
575
|
fi
|
|
373
576
|
fi
|
|
374
577
|
|
|
578
|
+
# --- Card close: first-session welcome or tutorial tip (visible) ---
|
|
579
|
+
# Everything the user should see renders before the hidden context below, so
|
|
580
|
+
# a harness that displays the card directly (bin/lib/card-delivery.sh) shows
|
|
581
|
+
# all of it. In that mode the card also asks the opening question itself,
|
|
582
|
+
# the same way bin/codex-session-start.sh ends the Codex and Pi card.
|
|
583
|
+
if [ -z "$FIRST_SESSION" ] && [ -f "$STATE_FILE" ]; then
|
|
584
|
+
FIRST_SESSION=$(jq -r '.first_session // false' "$STATE_FILE" 2>/dev/null)
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
if [ "$FIRST_SESSION" = "true" ]; then
|
|
588
|
+
echo ""
|
|
589
|
+
echo " Welcome! This is your first session."
|
|
590
|
+
if [ "${EGREGORE_CARD_DELIVERY:-text}" = "harness" ]; then
|
|
591
|
+
echo ""
|
|
592
|
+
echo " Want a quick tour first (/onboarding), or jump straight in?"
|
|
593
|
+
fi
|
|
594
|
+
# Clear the flag so it only shows once
|
|
595
|
+
jq '.first_session = false' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
596
|
+
else
|
|
597
|
+
# --- Tutorial tip (if onboarding done but tutorial not) ---
|
|
598
|
+
TUTORIAL_COMPLETE="true"
|
|
599
|
+
if [ -f "$STATE_FILE" ]; then
|
|
600
|
+
TUTORIAL_COMPLETE=$(jq -r '.tutorial_complete // false' "$STATE_FILE" 2>/dev/null || echo "true")
|
|
601
|
+
fi
|
|
602
|
+
|
|
603
|
+
if [ "$TUTORIAL_COMPLETE" != "true" ]; then
|
|
604
|
+
# A tip that has not been acted on after a few sessions is noise; the
|
|
605
|
+
# ledger retires it after NOTICE_TIP_MAX showings.
|
|
606
|
+
_notice tutorial-tip tip "tutorial" "tutorial" " Tip: Run /tutorial to learn the core loop."
|
|
607
|
+
fi
|
|
608
|
+
_notice_flush
|
|
609
|
+
if [ "${EGREGORE_CARD_DELIVERY:-text}" = "harness" ]; then
|
|
610
|
+
echo ""
|
|
611
|
+
echo " What are you working on?"
|
|
612
|
+
fi
|
|
613
|
+
fi
|
|
614
|
+
|
|
375
615
|
echo ""
|
|
376
616
|
|
|
377
617
|
# --- Session context (hidden, for Claude) ---
|
|
@@ -487,34 +727,21 @@ bash "$SCRIPT_DIR/bin/startup-check.sh" >/dev/null 2>&1 &
|
|
|
487
727
|
# Clean up temp files
|
|
488
728
|
rm -rf "$CTX_DIR"
|
|
489
729
|
|
|
490
|
-
# ---
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
730
|
+
# --- Instruction for the model ---
|
|
731
|
+
# Harness delivery: the user already sees the scripted card, question
|
|
732
|
+
# included, so the model must not retype it. Text delivery (a harness that
|
|
733
|
+
# cannot display hook output directly) keeps the display instruction. The
|
|
734
|
+
# branch rule that used to follow is gone: CLAUDE.md's "Before Project
|
|
735
|
+
# Changes" section is the one branching contract.
|
|
736
|
+
echo ""
|
|
737
|
+
if [ "${EGREGORE_CARD_DELIVERY:-text}" = "harness" ]; then
|
|
738
|
+
if [ "$FIRST_SESSION" = "true" ]; then
|
|
739
|
+
echo "STARTUP CARD: The harness has already shown the user the startup card above, ending with an offer of a quick tour (/onboarding). Do not reprint, summarize, or narrate it. Treat the user's first message as their answer."
|
|
740
|
+
else
|
|
741
|
+
echo "STARTUP CARD: The harness has already shown the user the startup card above, ending with \"What are you working on?\". Do not reprint, summarize, or narrate it. Treat the user's first message as the answer. If it carries no request (the launcher submits \`start\` automatically), reply with one short line and no tool calls."
|
|
742
|
+
fi
|
|
743
|
+
elif [ "$FIRST_SESSION" = "true" ]; then
|
|
499
744
|
echo "IMPORTANT: Display the above greeting exactly as-is (ASCII art + ornamented status). Then ask the user if they'd like a quick onboarding tour (run /onboarding), or if they want to jump straight in."
|
|
500
|
-
# Clear the flag so it only shows once
|
|
501
|
-
jq '.first_session = false' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
502
745
|
else
|
|
503
|
-
# --- Tutorial tip (if onboarding done but tutorial not) ---
|
|
504
|
-
TUTORIAL_COMPLETE="true"
|
|
505
|
-
if [ -f "$STATE_FILE" ]; then
|
|
506
|
-
TUTORIAL_COMPLETE=$(jq -r '.tutorial_complete // false' "$STATE_FILE" 2>/dev/null || echo "true")
|
|
507
|
-
fi
|
|
508
|
-
|
|
509
|
-
if [ "$TUTORIAL_COMPLETE" != "true" ]; then
|
|
510
|
-
# A tip that has not been acted on after a few sessions is noise; the
|
|
511
|
-
# ledger retires it after NOTICE_TIP_MAX showings.
|
|
512
|
-
_notice tutorial-tip tip "tutorial" "tutorial" " Tip: Run /tutorial to learn the core loop."
|
|
513
|
-
fi
|
|
514
|
-
_notice_flush
|
|
515
|
-
|
|
516
|
-
echo ""
|
|
517
746
|
echo "IMPORTANT: Display the above greeting to the user exactly as-is (preserve the ASCII art formatting and ornamented status) on their first message. Then ask: What are you working on?"
|
|
518
|
-
echo ""
|
|
519
|
-
echo "BRANCH RULE: When the user responds with what they're working on, your FIRST action is to create a working branch: git fetch origin $BASE_BRANCH --quiet && git checkout --no-track -b dev/{author}/{topic-slug} origin/$BASE_BRANCH. Do this BEFORE any other work. Derive the topic slug from their description. If they ask a pure question with no work intent, skip branching."
|
|
520
747
|
fi
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Handoff metadata for many files in one process.
|
|
3
|
+
|
|
4
|
+
bin/lib/handoff-meta.sh extracts a handoff's author, topic and date with a
|
|
5
|
+
grep | sed | xargs pipeline per field, and the startup gather of handoffs
|
|
6
|
+
addressed to the user added an awk, a read loop and a jq per file: over a
|
|
7
|
+
hundred processes for five handoffs. This module reads each file once and
|
|
8
|
+
reproduces those pipelines' results, including xargs' quote and whitespace
|
|
9
|
+
handling and the echo it runs, so the files the gather writes are
|
|
10
|
+
unchanged.
|
|
11
|
+
|
|
12
|
+
handoff_meta.py addressed NAMES_OUT RICH_OUT [FILE...]
|
|
13
|
+
|
|
14
|
+
writes the gather's two files: NAMES_OUT holds the handoff names, RICH_OUT
|
|
15
|
+
their author, topic, date and Repo State table rows. bin/tests/
|
|
16
|
+
test-handoff-metadata.sh holds this module to the shell functions.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
# POSIX [[:space:]]. On macOS, sed in a UTF-8 locale also counts Unicode
|
|
27
|
+
# spaces (a no-break space after "**Author**:" is stripped there).
|
|
28
|
+
_ASCII_SPACE = frozenset(" \t\n\v\f\r")
|
|
29
|
+
_DARWIN = sys.platform == "darwin"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _is_space(character: str) -> bool:
|
|
33
|
+
return character in _ASCII_SPACE or (_DARWIN and character.isspace())
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _lstrip_space(text: str) -> str:
|
|
37
|
+
position = 0
|
|
38
|
+
while position < len(text) and _is_space(text[position]):
|
|
39
|
+
position += 1
|
|
40
|
+
return text[position:]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _echo_operands(arguments: list[str]) -> list[str]:
|
|
44
|
+
"""What /bin/echo prints of its arguments, as xargs runs it."""
|
|
45
|
+
if _DARWIN:
|
|
46
|
+
# FreeBSD echo: only an exact first "-n" is an option, and a "\c"
|
|
47
|
+
# ending the last argument is dropped with the newline.
|
|
48
|
+
if arguments and arguments[0] == "-n":
|
|
49
|
+
arguments = arguments[1:]
|
|
50
|
+
if arguments and arguments[-1].endswith("\\c"):
|
|
51
|
+
arguments = [*arguments[:-1], arguments[-1][:-2]]
|
|
52
|
+
return arguments
|
|
53
|
+
# GNU echo: leading arguments made only of n, e and E are options.
|
|
54
|
+
while arguments and re.fullmatch(r"-[neE]+", arguments[0]):
|
|
55
|
+
arguments = arguments[1:]
|
|
56
|
+
return arguments
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _xargs(text: str) -> str:
|
|
60
|
+
"""`printf '%s\\n' "$text" | xargs`, as captured by $(...).
|
|
61
|
+
|
|
62
|
+
Blanks and newlines separate arguments, quotes group them, a backslash
|
|
63
|
+
outside quotes escapes the next character, and an unterminated quote
|
|
64
|
+
makes xargs fail before running echo.
|
|
65
|
+
"""
|
|
66
|
+
arguments: list[str] = []
|
|
67
|
+
argument: list[str] = []
|
|
68
|
+
started = False
|
|
69
|
+
quote = ""
|
|
70
|
+
characters = iter(text + "\n")
|
|
71
|
+
for character in characters:
|
|
72
|
+
if quote:
|
|
73
|
+
if character == quote:
|
|
74
|
+
quote = ""
|
|
75
|
+
elif character == "\n":
|
|
76
|
+
return ""
|
|
77
|
+
else:
|
|
78
|
+
argument.append(character)
|
|
79
|
+
continue
|
|
80
|
+
if character in " \t\n":
|
|
81
|
+
if started:
|
|
82
|
+
arguments.append("".join(argument))
|
|
83
|
+
argument, started = [], False
|
|
84
|
+
continue
|
|
85
|
+
started = True
|
|
86
|
+
if character in "'\"":
|
|
87
|
+
quote = character
|
|
88
|
+
elif character == "\\":
|
|
89
|
+
escaped = next(characters, None)
|
|
90
|
+
if escaped is None:
|
|
91
|
+
break
|
|
92
|
+
argument.append(escaped)
|
|
93
|
+
else:
|
|
94
|
+
argument.append(character)
|
|
95
|
+
if quote:
|
|
96
|
+
return ""
|
|
97
|
+
if started:
|
|
98
|
+
arguments.append("".join(argument))
|
|
99
|
+
return " ".join(_echo_operands(arguments)).rstrip("\n")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _bash_echo(value: str) -> str:
|
|
103
|
+
"""`echo "$value"` in bash: an argument of only n, e and E is an option."""
|
|
104
|
+
return "" if re.fullmatch(r"-[neE]+", value) else value
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _first(lines: list[str], prefix: str) -> str | None:
|
|
108
|
+
return next((line for line in lines if line.startswith(prefix)), None)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _after(lines: list[str], prefix: str) -> str:
|
|
112
|
+
"""grep -m1 "^prefix" | sed "s/^prefix[[:space:]]*//" | xargs"""
|
|
113
|
+
line = _first(lines, prefix)
|
|
114
|
+
return "" if line is None else _xargs(_lstrip_space(line[len(prefix):]))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _markdown_field(lines: list[str], key: str) -> str:
|
|
118
|
+
return _after(lines, f"**{key}**:") or _after(lines, f"**{key}:**")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _yaml_field(lines: list[str], key: str) -> str:
|
|
122
|
+
# sed -n '/^---$/,/^---$/p' prints every such range, not only the first.
|
|
123
|
+
inside = False
|
|
124
|
+
ranged: list[str] = []
|
|
125
|
+
for line in lines:
|
|
126
|
+
if inside:
|
|
127
|
+
ranged.append(line)
|
|
128
|
+
inside = line != "---"
|
|
129
|
+
elif line == "---":
|
|
130
|
+
ranged.append(line)
|
|
131
|
+
inside = True
|
|
132
|
+
line = _first(ranged, f"{key}:")
|
|
133
|
+
if line is None:
|
|
134
|
+
return ""
|
|
135
|
+
value = _lstrip_space(line[len(key) + 1:])
|
|
136
|
+
for mark in ('"', "'"):
|
|
137
|
+
value = value[1:] if value.startswith(mark) else value
|
|
138
|
+
value = value[:-1] if value.endswith(mark) else value
|
|
139
|
+
return _xargs(value)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _name(path: str) -> str:
|
|
143
|
+
name = os.path.basename(path)
|
|
144
|
+
return name[:-3] if name.endswith(".md") and name != ".md" else name
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _author_from_filename(path: str) -> str:
|
|
148
|
+
match = re.match(r"[0-9]{2}-([^-]+)-", _name(path)) or re.match(
|
|
149
|
+
r"[0-9]{4}-[0-9]{2}-[0-9]{2}-([^-]+)-", _name(path)
|
|
150
|
+
)
|
|
151
|
+
return match.group(1) if match else ""
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _date_from_filename(path: str) -> str:
|
|
155
|
+
name = _name(path)
|
|
156
|
+
directory = os.path.basename(os.path.dirname(path))
|
|
157
|
+
day = re.match(r"([0-9]{2})-", name)
|
|
158
|
+
if re.fullmatch(r"[0-9]{4}-[0-9]{2}", directory) and day:
|
|
159
|
+
return f"{directory}-{day.group(1)}"
|
|
160
|
+
dated = re.match(r"([0-9]{4}-[0-9]{2}-[0-9]{2})-", name)
|
|
161
|
+
return dated.group(1) if dated else ""
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def author(lines: list[str], path: str) -> str:
|
|
165
|
+
found = (
|
|
166
|
+
_markdown_field(lines, "Author")
|
|
167
|
+
or _markdown_field(lines, "From")
|
|
168
|
+
or _after(lines, "From:")
|
|
169
|
+
or _yaml_field(lines, "author")
|
|
170
|
+
or _yaml_field(lines, "from")
|
|
171
|
+
or _author_from_filename(path)
|
|
172
|
+
)
|
|
173
|
+
# sed 's/[[:space:]]*→.*//'
|
|
174
|
+
arrow = found.find("→")
|
|
175
|
+
if arrow >= 0:
|
|
176
|
+
while arrow > 0 and _is_space(found[arrow - 1]):
|
|
177
|
+
arrow -= 1
|
|
178
|
+
found = found[:arrow]
|
|
179
|
+
return _xargs(found)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def topic(lines: list[str]) -> str:
|
|
183
|
+
heading = _first(lines, "# ")
|
|
184
|
+
found = ""
|
|
185
|
+
if heading is not None:
|
|
186
|
+
text = heading[2:]
|
|
187
|
+
if text.startswith("Handoff:"):
|
|
188
|
+
text = _lstrip_space(text[len("Handoff:"):])
|
|
189
|
+
found = _xargs(text)
|
|
190
|
+
return found or _yaml_field(lines, "topic")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def date(lines: list[str], path: str) -> str:
|
|
194
|
+
return (
|
|
195
|
+
_markdown_field(lines, "Date")
|
|
196
|
+
or _after(lines, "Date:")
|
|
197
|
+
or _yaml_field(lines, "date")
|
|
198
|
+
or _date_from_filename(path)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _json_string(value: str) -> str:
|
|
203
|
+
# jq's compact output: raw UTF-8, with DEL escaped as well.
|
|
204
|
+
return json.dumps(value, ensure_ascii=False).replace("\x7f", "\\u007f")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _cell(fields: list[str], position: int) -> str:
|
|
208
|
+
return _xargs(_bash_echo(fields[position] if position < len(fields) else ""))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def repo_state(lines: list[str]) -> str:
|
|
212
|
+
"""The `## Repo State` table as the JSON array the gather builds.
|
|
213
|
+
|
|
214
|
+
Each row was printed into JSON text and parsed with `jq -s`, so a cell
|
|
215
|
+
jq could not parse empties the whole table, as it did there.
|
|
216
|
+
"""
|
|
217
|
+
rows: list[str] = []
|
|
218
|
+
found = False
|
|
219
|
+
for line in lines:
|
|
220
|
+
if line.startswith("## Repo State"):
|
|
221
|
+
found = True
|
|
222
|
+
continue
|
|
223
|
+
if not found:
|
|
224
|
+
continue
|
|
225
|
+
if line.startswith("#"):
|
|
226
|
+
break
|
|
227
|
+
if len(line) > 1 and line[0] == "|" and line[1] != "-" and not line.startswith("| Repo"):
|
|
228
|
+
rows.append(line)
|
|
229
|
+
entries: list[str] = []
|
|
230
|
+
for row in rows:
|
|
231
|
+
fields = row.split("|")
|
|
232
|
+
repository, branch, base = _cell(fields, 1), _cell(fields, 2), _cell(fields, 4)
|
|
233
|
+
pull = _cell(fields, 3)
|
|
234
|
+
pull = (pull[1:] if pull.startswith("#") else pull).replace("—", "", 1)
|
|
235
|
+
if not repository:
|
|
236
|
+
continue
|
|
237
|
+
number = "null"
|
|
238
|
+
if re.fullmatch(r"[+-]?[0-9]+", pull) and -(2**63) <= int(pull) < 2**63:
|
|
239
|
+
number = ("-" if pull.startswith("-") else "") + (pull.lstrip("+-").lstrip("0") or "0")
|
|
240
|
+
try:
|
|
241
|
+
text = [json.loads(f'"{value}"') for value in (repository, branch, base)]
|
|
242
|
+
except ValueError:
|
|
243
|
+
return "[]"
|
|
244
|
+
entries.append(
|
|
245
|
+
f'{{"repo":{_json_string(text[0])},"branch":{_json_string(text[1])},'
|
|
246
|
+
f'"pr":{number},"base":{_json_string(text[2])}}}'
|
|
247
|
+
)
|
|
248
|
+
return "[" + ",".join(entries) + "]"
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _lines(path: str) -> list[str]:
|
|
252
|
+
try:
|
|
253
|
+
with open(path, "rb") as handle:
|
|
254
|
+
content = handle.read().decode("utf-8", errors="replace")
|
|
255
|
+
except OSError:
|
|
256
|
+
return []
|
|
257
|
+
lines = content.split("\n")
|
|
258
|
+
if lines and lines[-1] == "":
|
|
259
|
+
lines.pop()
|
|
260
|
+
return lines
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def addressed(paths: list[str]) -> tuple[str, str]:
|
|
264
|
+
names: list[str] = []
|
|
265
|
+
rich: list[str] = []
|
|
266
|
+
for path in paths:
|
|
267
|
+
lines = _lines(path)
|
|
268
|
+
name = _name(path)
|
|
269
|
+
names.append(f'"{name}"')
|
|
270
|
+
try:
|
|
271
|
+
entry = (
|
|
272
|
+
f'{{"name":{_json_string(name)},"author":{_json_string(author(lines, path))},'
|
|
273
|
+
f'"topic":{_json_string(topic(lines))},"date":{_json_string(date(lines, path))},'
|
|
274
|
+
f'"repoState":{repo_state(lines)}}}'
|
|
275
|
+
)
|
|
276
|
+
except Exception: # one unreadable handoff keeps its place, not the list's
|
|
277
|
+
entry = f'{{"name":{_json_string(name)},"author":"","topic":"","date":"","repoState":[]}}'
|
|
278
|
+
rich.append(entry)
|
|
279
|
+
return "[" + ",".join(names) + "]", "[" + ",".join(rich) + "]"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def main(arguments: list[str]) -> int:
|
|
283
|
+
if len(arguments) < 3 or arguments[0] != "addressed":
|
|
284
|
+
print("usage: handoff_meta.py addressed NAMES_OUT RICH_OUT [FILE...]", file=sys.stderr)
|
|
285
|
+
return 2
|
|
286
|
+
names, rich = addressed(arguments[3:])
|
|
287
|
+
for path, content in ((arguments[1], names), (arguments[2], rich)):
|
|
288
|
+
with open(path, "w", encoding="utf-8") as handle:
|
|
289
|
+
handle.write(content + "\n")
|
|
290
|
+
return 0
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
raise SystemExit(main(sys.argv[1:]))
|