create-egregore 0.19.33 → 0.20.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 +118 -13
- 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 +45 -3
- package/package.json +4 -3
- package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/spec-manifest.json +3 -3
- package/runtime/codex/AGENTS.md +11 -4
- package/runtime/codex/bin/activity-data.sh +4 -8
- package/runtime/codex/bin/attendant.sh +13 -0
- package/runtime/codex/bin/autosave.sh +27 -1
- package/runtime/codex/bin/codex-render-spec.mjs +6 -0
- package/runtime/codex/bin/codex-session-start.sh +3 -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/ingest.py +81 -5
- package/runtime/codex/bin/lib/context.sh +26 -1
- package/runtime/codex/bin/lib/greeting.sh +4 -1
- package/runtime/codex/bin/lib/worktree-links.sh +25 -0
- package/runtime/codex/manifest.json +7 -1
- package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/spec-manifest.json +3 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
- package/runtime/pi/.pi/extensions/egregore.ts +17 -6
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +11 -4
- package/runtime/pi/bin/activity-data.sh +4 -8
- package/runtime/pi/bin/attendant.sh +13 -0
- package/runtime/pi/bin/autosave.sh +27 -1
- package/runtime/pi/bin/codex-render-spec.mjs +6 -0
- package/runtime/pi/bin/codex-session-start.sh +3 -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/ingest.py +81 -5
- package/runtime/pi/bin/lib/context.sh +26 -1
- 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/manifest.json +7 -1
- package/runtime/prime/.claude/context/notification-consent.md +66 -0
- package/runtime/prime/.codex/config.toml +17 -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-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 +125 -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 +393 -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 +489 -0
- package/runtime/prime/bin/activity-data.sh +238 -0
- package/runtime/prime/bin/agent.sh +775 -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 +440 -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 +232 -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 +671 -0
- package/runtime/prime/bin/handoff-save-egregore.sh +212 -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 +756 -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-sync.sh +406 -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 +123 -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 +209 -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 +178 -0
|
@@ -23,10 +23,12 @@ from datetime import datetime, timezone
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from xml.etree import ElementTree
|
|
25
25
|
|
|
26
|
-
sys.path.
|
|
26
|
+
sys.path.append(str(Path(__file__).resolve().parent))
|
|
27
|
+
import catalogue as catalogue_module # noqa: E402
|
|
27
28
|
import doc_segment # noqa: E402 (same-directory module, path set above)
|
|
28
29
|
import pdf_extract # noqa: E402
|
|
29
30
|
import source_profile # noqa: E402
|
|
31
|
+
import text_repair # noqa: E402
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
ROOT = Path(__file__).resolve().parent.parent
|
|
@@ -152,6 +154,35 @@ def chunks(text: str, limit: int = 7000) -> list[str]:
|
|
|
152
154
|
return result
|
|
153
155
|
|
|
154
156
|
|
|
157
|
+
def find_publisher_profile() -> Path | None:
|
|
158
|
+
"""Where this instance's publisher profile is, if it has one.
|
|
159
|
+
|
|
160
|
+
The profile is what assigns a zone and a trust tier to each document, and
|
|
161
|
+
zone is a hard retrieval boundary. Ingesting without it produces a corpus
|
|
162
|
+
where every document is unplaced, so every later question is refused — a
|
|
163
|
+
failure that looks like an empty archive rather than a missing setting.
|
|
164
|
+
|
|
165
|
+
So this looks in the places a profile actually lives rather than in one
|
|
166
|
+
fixed path. An instance keeps its ontology under memory/knowledge/tools/,
|
|
167
|
+
not beside its intake metadata, and requiring an environment variable to
|
|
168
|
+
bridge that gap means the common case is the broken one.
|
|
169
|
+
"""
|
|
170
|
+
override = os.environ.get("EGREGORE_PUBLISHER_PROFILE")
|
|
171
|
+
if override:
|
|
172
|
+
path = Path(override)
|
|
173
|
+
return path if path.is_file() else None
|
|
174
|
+
|
|
175
|
+
candidates = [metadata_root() / "publisher-profile.json"]
|
|
176
|
+
memory = Path("memory")
|
|
177
|
+
if memory.is_dir():
|
|
178
|
+
candidates.extend(sorted(memory.glob("knowledge/tools/*/publisher-profile.json")))
|
|
179
|
+
candidates.append(memory / "publisher-profile.json")
|
|
180
|
+
for path in candidates:
|
|
181
|
+
if path.is_file():
|
|
182
|
+
return path
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
|
|
155
186
|
def publisher_profile() -> dict | None:
|
|
156
187
|
"""Load the instance's publisher profile, if one is configured.
|
|
157
188
|
|
|
@@ -159,9 +190,8 @@ def publisher_profile() -> dict | None:
|
|
|
159
190
|
operator set: deriving a region is an inference, and an inference must not
|
|
160
191
|
silently replace a stated fact.
|
|
161
192
|
"""
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if not path.is_file():
|
|
193
|
+
path = find_publisher_profile()
|
|
194
|
+
if path is None:
|
|
165
195
|
return None
|
|
166
196
|
try:
|
|
167
197
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
@@ -223,7 +253,19 @@ def add_path(args: argparse.Namespace) -> dict:
|
|
|
223
253
|
existing_manifest = load_manifest(directory)
|
|
224
254
|
previous = {d["id"]: d for d in existing_manifest.get("documents", [])}
|
|
225
255
|
now = datetime.now(timezone.utc).isoformat()
|
|
256
|
+
profile_path = find_publisher_profile()
|
|
226
257
|
profile = publisher_profile()
|
|
258
|
+
if profile is None:
|
|
259
|
+
# Said once, plainly, because the alternative is a corpus that ingests
|
|
260
|
+
# cleanly and then refuses every question with no indication why.
|
|
261
|
+
print("ingest: no publisher-profile.json found — documents will carry no zone "
|
|
262
|
+
"or trust tier, and a zoneless document can never be served. Put one "
|
|
263
|
+
"under memory/knowledge/tools/<ontology>/ or set "
|
|
264
|
+
"EGREGORE_PUBLISHER_PROFILE.", file=sys.stderr)
|
|
265
|
+
catalogue = catalogue_module.load(
|
|
266
|
+
profile or {}, base=profile_path.parent if profile_path else None
|
|
267
|
+
)
|
|
268
|
+
repair_map = text_repair.load_mapping(profile or {})
|
|
227
269
|
ingested = unchanged = metadata_updated = skipped = pruned = 0
|
|
228
270
|
errors: list[dict] = []
|
|
229
271
|
seen_paths: set[str] = set()
|
|
@@ -252,6 +294,15 @@ def add_path(args: argparse.Namespace) -> dict:
|
|
|
252
294
|
extraction = {"kind": "local", "extractor": extractor} if extractor else None
|
|
253
295
|
if extraction and detail:
|
|
254
296
|
extraction["pages"] = detail
|
|
297
|
+
# Fix characters the document's own font encoding got wrong, before
|
|
298
|
+
# anything downstream indexes or quotes them.
|
|
299
|
+
repaired = None
|
|
300
|
+
if text and repair_map:
|
|
301
|
+
outcome = text_repair.repair(text, repair_map)
|
|
302
|
+
if outcome.replaced:
|
|
303
|
+
text = outcome.text
|
|
304
|
+
repaired = outcome.as_report()
|
|
305
|
+
|
|
255
306
|
if error or text is None or not text.strip():
|
|
256
307
|
skipped += 1
|
|
257
308
|
errors.append({"path": relative, "error": error or "empty extracted text"})
|
|
@@ -283,6 +334,9 @@ def add_path(args: argparse.Namespace) -> dict:
|
|
|
283
334
|
continue
|
|
284
335
|
# Segment before chunking so no passage straddles two works. A document
|
|
285
336
|
# holding a single work yields one segment, and chunking is unchanged.
|
|
337
|
+
if repaired and extraction:
|
|
338
|
+
extraction["repair"] = repaired
|
|
339
|
+
|
|
286
340
|
segments = doc_segment.segment(text)
|
|
287
341
|
document_chunks: list[tuple[str, doc_segment.Segment]] = []
|
|
288
342
|
for seg in segments:
|
|
@@ -314,12 +368,34 @@ def add_path(args: argparse.Namespace) -> dict:
|
|
|
314
368
|
# is absent filters nothing. A derived value is marked as derived: anything
|
|
315
369
|
# downstream can tell it apart from one a person stated, and weigh it by the
|
|
316
370
|
# confidence recorded alongside. An operator's value is never overwritten.
|
|
371
|
+
# Evidence strongest first: what the operator stated, then what the
|
|
372
|
+
# organisation's own catalogue records, then what can be inferred from
|
|
373
|
+
# the document's origin. A weaker source never overwrites a stronger one.
|
|
317
374
|
provenance = None
|
|
318
375
|
doc_boundaries = dict(boundaries)
|
|
376
|
+
catalogued = catalogue_module.lookup(catalogue, relative)
|
|
377
|
+
|
|
378
|
+
if catalogued:
|
|
379
|
+
provenance = {"from_catalogue": {k: v for k, v in catalogued.items()}}
|
|
380
|
+
for field_name, value in catalogued.items():
|
|
381
|
+
if field_name in ("zone", "crop", "topic", "customer", "jurisdiction", "project"):
|
|
382
|
+
if field_name not in doc_boundaries:
|
|
383
|
+
doc_boundaries[field_name] = value
|
|
384
|
+
provenance["applied_boundary"] = field_name
|
|
385
|
+
if catalogued.get("year"):
|
|
386
|
+
structure.setdefault("publication_year", catalogued["year"])
|
|
387
|
+
structure["year_source"] = structure.get("year_source") or "catalogue"
|
|
388
|
+
if catalogued.get("tier"):
|
|
389
|
+
provenance["tier"] = catalogued["tier"]
|
|
390
|
+
provenance["confidence"] = "high"
|
|
391
|
+
|
|
319
392
|
if profile:
|
|
320
393
|
resolved = source_profile.resolve(profile, filename=relative)
|
|
321
394
|
if resolved.publisher_class or resolved.notes:
|
|
322
|
-
|
|
395
|
+
inferred = resolved.as_dict()
|
|
396
|
+
# Keep the inference visible even when the catalogue answered,
|
|
397
|
+
# so a disagreement between the two is inspectable.
|
|
398
|
+
provenance = {**inferred, **(provenance or {})} if provenance else inferred
|
|
323
399
|
key = source_profile.boundary_key(profile)
|
|
324
400
|
if key and resolved.zone and key not in doc_boundaries:
|
|
325
401
|
doc_boundaries[key] = resolved.zone
|
|
@@ -370,6 +370,29 @@ fi
|
|
|
370
370
|
JSON="[]"
|
|
371
371
|
RICH="[]"
|
|
372
372
|
if [ -d "$SCRIPT_DIR/memory/handoffs" ]; then
|
|
373
|
+
# One definition everywhere: in connected mode the list comes from the
|
|
374
|
+
# graph's open-handoffs named read (open = pending/unread/read/claimed,
|
|
375
|
+
# matched by name/github/aliases) — the same definition the dashboard
|
|
376
|
+
# uses. The file grep below stays as the local-mode/offline fallback.
|
|
377
|
+
ADDRESSED=""
|
|
378
|
+
if [ "${LOCAL_MODE:-false}" != "true" ]; then
|
|
379
|
+
_GRAPH_LIST=$(bash "$SCRIPT_DIR/bin/graph-op.sh" open-handoffs "$AUTHOR" 5 2>/dev/null || true)
|
|
380
|
+
if echo "$_GRAPH_LIST" | jq -e '.values | length > 0' >/dev/null 2>&1; then
|
|
381
|
+
ADDRESSED=$(echo "$_GRAPH_LIST" | jq -r '.values[][0]' 2>/dev/null | awk '!seen[$0]++' | while IFS= read -r _SID; do
|
|
382
|
+
# Two on-disk layouts: dated subdirs (YYYY-MM/DD-slug.md, the id's
|
|
383
|
+
# own shape) and flat files at the handoffs root. Probe both, else
|
|
384
|
+
# a graph-listed handoff silently vanishes from the greeting.
|
|
385
|
+
_MONTH="${_SID:0:7}"
|
|
386
|
+
_REST="${_SID:8}"
|
|
387
|
+
_F="$SCRIPT_DIR/memory/handoffs/$_MONTH/$_REST.md"
|
|
388
|
+
if [ -f "$_F" ]; then
|
|
389
|
+
echo "$_F"
|
|
390
|
+
elif [ -f "$SCRIPT_DIR/memory/handoffs/$_SID.md" ]; then
|
|
391
|
+
echo "$SCRIPT_DIR/memory/handoffs/$_SID.md"
|
|
392
|
+
fi
|
|
393
|
+
done | head -5)
|
|
394
|
+
fi
|
|
395
|
+
fi
|
|
373
396
|
# Match github username, display name, github_name first word, and people-file name
|
|
374
397
|
# Handoff files may use "to: name", "**To**: name", or "To: name"
|
|
375
398
|
_DISPLAY=$(jq -r '.display_name // empty' "$STATE_FILE" 2>/dev/null)
|
|
@@ -387,7 +410,9 @@ fi
|
|
|
387
410
|
echo "$_GREP_PAT" | grep -qF "$_VARIANT" && continue
|
|
388
411
|
_GREP_PAT="$_GREP_PAT\|[Tt]o[*]*: *$_VARIANT\|[Tt]o[*]*:$_VARIANT"
|
|
389
412
|
done
|
|
390
|
-
|
|
413
|
+
if [ -z "$ADDRESSED" ]; then
|
|
414
|
+
ADDRESSED=$(grep -rli "$_GREP_PAT" "$SCRIPT_DIR/memory/handoffs/" 2>/dev/null | sort -r | head -5 || true)
|
|
415
|
+
fi
|
|
391
416
|
JSON="["
|
|
392
417
|
RICH="["
|
|
393
418
|
FIRST=true
|
|
@@ -188,7 +188,10 @@ fi
|
|
|
188
188
|
# Staged auto-saves awaiting the user's merge (the /autosave consent gate).
|
|
189
189
|
# Live gh query so the line persists until the PRs actually merge — the
|
|
190
190
|
# ledger cursor above only reports once. Fail-soft when gh/network is out.
|
|
191
|
-
|
|
191
|
+
# Shown only while the autosave experiment is enabled: stale PRs from a
|
|
192
|
+
# disabled experiment should not nag every greeting.
|
|
193
|
+
AUTOSAVE_ON=$(jq -r '.autosave_enabled // false' "$STATE_FILE" 2>/dev/null)
|
|
194
|
+
if [ "$AUTOSAVE_ON" = "true" ] && command -v gh >/dev/null 2>&1; then
|
|
192
195
|
GH_SELF=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
|
|
193
196
|
if [ -n "$GH_SELF" ]; then
|
|
194
197
|
WAITING_AS=$(gh pr list --state open --author "$GH_SELF" --limit 20 \
|
|
@@ -52,3 +52,28 @@ egregore_link_shared_state() {
|
|
|
52
52
|
fi
|
|
53
53
|
done
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
# Resolve the .env that governs a checkout. A worktree can be missing its .env
|
|
57
|
+
# symlink (created before .env existed, or by a runtime that skipped worktree
|
|
58
|
+
# setup) — self-heal the links, then fall back to the main checkout's .env so
|
|
59
|
+
# credential loading never silently comes up empty. Prints the usable path, or
|
|
60
|
+
# nothing when neither checkout has one. Always returns 0.
|
|
61
|
+
egregore_env_file() {
|
|
62
|
+
local project_dir="${1:?project dir required}"
|
|
63
|
+
if [ ! -f "$project_dir/.env" ] && [ -f "$project_dir/.git" ]; then
|
|
64
|
+
egregore_link_shared_state "$project_dir" 2>/dev/null || true
|
|
65
|
+
fi
|
|
66
|
+
if [ -f "$project_dir/.env" ]; then
|
|
67
|
+
echo "$project_dir/.env"
|
|
68
|
+
return 0
|
|
69
|
+
fi
|
|
70
|
+
if [ -f "$project_dir/.git" ]; then
|
|
71
|
+
local main_dir
|
|
72
|
+
main_dir="$(egregore_main_project_dir "$project_dir" 2>/dev/null || true)"
|
|
73
|
+
if [ -n "$main_dir" ] && [ -f "$main_dir/.env" ]; then
|
|
74
|
+
echo "$main_dir/.env"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
return 0
|
|
79
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "egregore-runtime",
|
|
3
3
|
"policy": "available OSS components selected by capability-distribution.json; Connect is an authenticated additive overlay",
|
|
4
|
-
"skillCount":
|
|
4
|
+
"skillCount": 46,
|
|
5
5
|
"skills": [
|
|
6
6
|
"activity",
|
|
7
7
|
"announce",
|
|
@@ -19,12 +19,14 @@
|
|
|
19
19
|
"handoff",
|
|
20
20
|
"harvest",
|
|
21
21
|
"ingest",
|
|
22
|
+
"ingest-notion",
|
|
22
23
|
"ingest-user-interview",
|
|
23
24
|
"invite",
|
|
24
25
|
"issue",
|
|
25
26
|
"me",
|
|
26
27
|
"meeting",
|
|
27
28
|
"note",
|
|
29
|
+
"notion-connect",
|
|
28
30
|
"onboarding",
|
|
29
31
|
"pr",
|
|
30
32
|
"pull",
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
"slack-connect",
|
|
37
39
|
"summon",
|
|
38
40
|
"sync-repos",
|
|
41
|
+
"teams-connect",
|
|
39
42
|
"telegram-connect",
|
|
40
43
|
"telemetry",
|
|
41
44
|
"test",
|
|
@@ -71,6 +74,7 @@
|
|
|
71
74
|
".codex/skills/handoff/SKILL.md",
|
|
72
75
|
".codex/skills/harvest/QUESTION_PALETTE.md",
|
|
73
76
|
".codex/skills/harvest/SKILL.md",
|
|
77
|
+
".codex/skills/ingest-notion/SKILL.md",
|
|
74
78
|
".codex/skills/ingest-user-interview/SKILL.md",
|
|
75
79
|
".codex/skills/ingest/SKILL.md",
|
|
76
80
|
".codex/skills/invite/SKILL.md",
|
|
@@ -78,6 +82,7 @@
|
|
|
78
82
|
".codex/skills/me/SKILL.md",
|
|
79
83
|
".codex/skills/meeting/SKILL.md",
|
|
80
84
|
".codex/skills/note/SKILL.md",
|
|
85
|
+
".codex/skills/notion-connect/SKILL.md",
|
|
81
86
|
".codex/skills/onboarding/SKILL.md",
|
|
82
87
|
".codex/skills/pr/SKILL.md",
|
|
83
88
|
".codex/skills/pull/SKILL.md",
|
|
@@ -89,6 +94,7 @@
|
|
|
89
94
|
".codex/skills/slack-connect/SKILL.md",
|
|
90
95
|
".codex/skills/summon/SKILL.md",
|
|
91
96
|
".codex/skills/sync-repos/SKILL.md",
|
|
97
|
+
".codex/skills/teams-connect/SKILL.md",
|
|
92
98
|
".codex/skills/telegram-connect/SKILL.md",
|
|
93
99
|
".codex/skills/telemetry/SKILL.md",
|
|
94
100
|
".codex/skills/test/SKILL.md",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ingest-notion
|
|
3
|
+
description: 'Fallback Codex adapter for the Egregore ingest-notion workflow.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- generated-by: bin/codex-sync-skills.sh -->
|
|
7
|
+
|
|
8
|
+
# Egregore ingest-notion Adapter
|
|
9
|
+
|
|
10
|
+
This is fallback coverage for a long-tail Egregore workflow that has not been
|
|
11
|
+
ported to a hand-written Codex-native skill yet.
|
|
12
|
+
|
|
13
|
+
Use the project shell and filesystem directly. Do not invoke Claude Code
|
|
14
|
+
commands. Translate interactive choices to structured Codex question tooling
|
|
15
|
+
when it is available; otherwise render compact numbered choices with an
|
|
16
|
+
`Other:` option and wait for the user.
|
|
17
|
+
|
|
18
|
+
1. Read `.claude/skills/ingest-notion/SKILL.md` for the workflow details.
|
|
19
|
+
2. Run the referenced `bin/` scripts directly from Codex.
|
|
20
|
+
3. Treat graph and publish steps as best-effort unless that workflow explicitly
|
|
21
|
+
says they are required.
|
|
22
|
+
4. For every external notification, follow
|
|
23
|
+
`.claude/context/notification-consent.md`: plan without sending, then show
|
|
24
|
+
a separate exact Send / Edit / Cancel checkpoint. Never infer notification
|
|
25
|
+
consent from the workflow request or a batch approval.
|
|
26
|
+
5. Keep local-mode behavior filesystem-first and avoid graph or notification
|
|
27
|
+
calls when `egregore.json` declares `"mode": "local"`.
|
|
28
|
+
6. Never call the deprecated `egregore-handoff` CLI for Egregore project
|
|
29
|
+
handoffs.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notion-connect
|
|
3
|
+
description: 'Fallback Codex adapter for the Egregore notion-connect workflow.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- generated-by: bin/codex-sync-skills.sh -->
|
|
7
|
+
|
|
8
|
+
# Egregore notion-connect Adapter
|
|
9
|
+
|
|
10
|
+
This is fallback coverage for a long-tail Egregore workflow that has not been
|
|
11
|
+
ported to a hand-written Codex-native skill yet.
|
|
12
|
+
|
|
13
|
+
Use the project shell and filesystem directly. Do not invoke Claude Code
|
|
14
|
+
commands. Translate interactive choices to structured Codex question tooling
|
|
15
|
+
when it is available; otherwise render compact numbered choices with an
|
|
16
|
+
`Other:` option and wait for the user.
|
|
17
|
+
|
|
18
|
+
1. Read `.claude/skills/notion-connect/SKILL.md` for the workflow details.
|
|
19
|
+
2. Run the referenced `bin/` scripts directly from Codex.
|
|
20
|
+
3. Treat graph and publish steps as best-effort unless that workflow explicitly
|
|
21
|
+
says they are required.
|
|
22
|
+
4. For every external notification, follow
|
|
23
|
+
`.claude/context/notification-consent.md`: plan without sending, then show
|
|
24
|
+
a separate exact Send / Edit / Cancel checkpoint. Never infer notification
|
|
25
|
+
consent from the workflow request or a batch approval.
|
|
26
|
+
5. Keep local-mode behavior filesystem-first and avoid graph or notification
|
|
27
|
+
calls when `egregore.json` declares `"mode": "local"`.
|
|
28
|
+
6. Never call the deprecated `egregore-handoff` CLI for Egregore project
|
|
29
|
+
handoffs.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: teams-connect
|
|
3
|
+
description: 'Fallback Codex adapter for the Egregore teams-connect workflow.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- generated-by: bin/codex-sync-skills.sh -->
|
|
7
|
+
|
|
8
|
+
# Egregore teams-connect Adapter
|
|
9
|
+
|
|
10
|
+
This is fallback coverage for a long-tail Egregore workflow that has not been
|
|
11
|
+
ported to a hand-written Codex-native skill yet.
|
|
12
|
+
|
|
13
|
+
Use the project shell and filesystem directly. Do not invoke Claude Code
|
|
14
|
+
commands. Translate interactive choices to structured Codex question tooling
|
|
15
|
+
when it is available; otherwise render compact numbered choices with an
|
|
16
|
+
`Other:` option and wait for the user.
|
|
17
|
+
|
|
18
|
+
1. Read `.claude/skills/teams-connect/SKILL.md` for the workflow details.
|
|
19
|
+
2. Run the referenced `bin/` scripts directly from Codex.
|
|
20
|
+
3. Treat graph and publish steps as best-effort unless that workflow explicitly
|
|
21
|
+
says they are required.
|
|
22
|
+
4. For every external notification, follow
|
|
23
|
+
`.claude/context/notification-consent.md`: plan without sending, then show
|
|
24
|
+
a separate exact Send / Edit / Cancel checkpoint. Never infer notification
|
|
25
|
+
consent from the workflow request or a batch approval.
|
|
26
|
+
5. Keep local-mode behavior filesystem-first and avoid graph or notification
|
|
27
|
+
calls when `egregore.json` declares `"mode": "local"`.
|
|
28
|
+
6. Never call the deprecated `egregore-handoff` CLI for Egregore project
|
|
29
|
+
handoffs.
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
{
|
|
36
36
|
"section": "After Greeting — BRANCH ON FIRST RESPONSE",
|
|
37
37
|
"action": "replace",
|
|
38
|
-
"sourceHash": "
|
|
38
|
+
"sourceHash": "d28a5c162ab4",
|
|
39
39
|
"note": "EnterWorktree -> bin/agent.sh branch with git checkout fallback; AskUserQuestion -> numbered options in plain text; branch-guard mapped to .codex/hooks/branch-guard.js with instruction fallback; plan-mode note dropped (no plan mode on Codex); /branch + /onboarding -> skill tokens. Handoff claiming and repoState auto-checkout kept verbatim (pure git)."
|
|
40
40
|
},
|
|
41
41
|
{
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
{
|
|
102
102
|
"section": "Command Awareness",
|
|
103
103
|
"action": "keep",
|
|
104
|
-
"sourceHash": "
|
|
104
|
+
"sourceHash": "be00d1fc715f",
|
|
105
105
|
"note": "Prepended the Codex command surface (skill tokens, native vs adapter split, $save abstraction); disambiguation map kept with /x -> $x token rendering. Tokens with no Codex skill (e.g. /loop, a Claude Code harness feature) are left as-is."
|
|
106
106
|
},
|
|
107
107
|
{
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
{
|
|
120
120
|
"section": "Mode",
|
|
121
121
|
"action": "keep",
|
|
122
|
-
"sourceHash": "
|
|
122
|
+
"sourceHash": "0f74e18fd1b4",
|
|
123
123
|
"note": "Kept; /env and /checkup rendered as $skill tokens."
|
|
124
124
|
},
|
|
125
125
|
{
|
|
@@ -355,8 +355,9 @@ Egregore runs in one of two configurations, set by `mode` in `egregore.json`. De
|
|
|
355
355
|
|
|
356
356
|
**Hard rules in local mode:**
|
|
357
357
|
- Never tell the user to "ask their admin" for credentials. The user IS the admin.
|
|
358
|
-
- Never surface `api_url`, `EGREGORE_API_KEY`, or
|
|
359
|
-
-
|
|
358
|
+
- Never surface `api_url`, `EGREGORE_API_KEY`, or config edits as an upgrade path. Hosted Egregore is not something a user can turn on by adding fields to `egregore.json`.
|
|
359
|
+
- **The one sanctioned upgrade path is `egregore connect`** (the launcher flow: registers the org, provisions the key, replays the graph). Connect-tier connector skills carry an explicit upsell gate for local instances — deliver their message verbatim, offer the upgrade via `egregore connect` or a clean "not now", and stop on decline. Beyond that gate, do not improvise: no hand-set `api_url`, no partial flows.
|
|
360
|
+
- If a feature requires the hosted service and has no upsell gate, say so plainly ("this isn't available in this configuration") and stop.
|
|
360
361
|
- Calling `bin/graph.sh` or `bin/notify.sh` in local mode is harmless — they fail soft and return empty results — but there is no need to call them; prefer reading `memory/` directly.
|
|
361
362
|
|
|
362
363
|
**Connected mode** (`"mode": "connected"`, `api_url` set) — the hosted configuration, used by organizations on the hosted service. Full feature set: Neo4j knowledge graph via `bin/graph.sh`, Telegram notifications via `bin/notify.sh`, dashboard publication, API-backed context gathering on session start. Use `/env` to check API key, `/checkup` for diagnostics. If the graph is offline, show troubleshooting.
|
|
@@ -2,10 +2,12 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
|
-
import { isMutatingBash, isRuntimeStatePath, mutatesOnlyRuntimeState } from "../../bin/pi-branch-policy.mjs";
|
|
5
|
+
import { isConsentCommand, isMutatingBash, isRuntimeStatePath, mutatesOnlyRuntimeState } from "../../bin/pi-branch-policy.mjs";
|
|
6
6
|
import {
|
|
7
|
+
activityDataPath,
|
|
7
8
|
captureSessionEnd,
|
|
8
9
|
createHandoff,
|
|
10
|
+
lastActivityJson,
|
|
9
11
|
parseViewRequest,
|
|
10
12
|
publishScroll,
|
|
11
13
|
renderActivity,
|
|
@@ -220,9 +222,21 @@ export default function egregore(pi) {
|
|
|
220
222
|
const focus = await ctx.ui.select("Focus", ["Handoffs", "Questions", "Recent work", "Dashboard", "Done"]);
|
|
221
223
|
if (!focus || focus === "Done") return;
|
|
222
224
|
const command = focus === "Dashboard" ? "dashboard" : "activity";
|
|
225
|
+
// Hand the model the complete data the extension already fetched, as a
|
|
226
|
+
// file: one fast read, never truncated, no reason to re-run the data
|
|
227
|
+
// script or spelunk renderer sources.
|
|
228
|
+
const cached = lastActivityJson();
|
|
223
229
|
const prompt = focus === "Dashboard"
|
|
224
230
|
? workflowPrompt("dashboard", "")
|
|
225
|
-
:
|
|
231
|
+
: [
|
|
232
|
+
`Continue the Egregore /activity workflow with focus: ${focus.toLowerCase()}.`,
|
|
233
|
+
"Output nothing except the final rendered result — no prose, planning, or narration between commands.",
|
|
234
|
+
cached
|
|
235
|
+
? `The complete activity data is already fresh at ${relative(root, activityDataPath(root))} — read that file in one call; do NOT re-run bin/activity-data.sh, read renderer sources, or run other data commands.`
|
|
236
|
+
: "",
|
|
237
|
+
"If required data is missing or a command fails, render the single line '◦ activity data unavailable' and stop — never debug data plumbing inside this workflow.",
|
|
238
|
+
"Rows without a topic: show the branch name instead; with neither, omit the row — never print (untitled).",
|
|
239
|
+
].filter(Boolean).join("\n");
|
|
226
240
|
sendQuietWorkflow(command, "", ctx, { prompt, working: `Opening ${focus.toLowerCase()}…` });
|
|
227
241
|
},
|
|
228
242
|
});
|
|
@@ -398,10 +412,7 @@ export default function egregore(pi) {
|
|
|
398
412
|
// (echo 'develop' into .egregore-branch-consent) contains a redirect and
|
|
399
413
|
// would otherwise be blocked by this very gate.
|
|
400
414
|
if (event.toolName === "bash") {
|
|
401
|
-
|
|
402
|
-
"^\\s*echo\\s+['\"]?" + branch + "['\"]?\\s*>\\s*\\.egregore-branch-consent\\s*$"
|
|
403
|
-
);
|
|
404
|
-
if (consentCmd.test(String(event.input?.command || "").trim())) return;
|
|
415
|
+
if (isConsentCommand(String(event.input?.command || "").trim(), branch)) return;
|
|
405
416
|
}
|
|
406
417
|
|
|
407
418
|
const consentFile = join(gitDir, ".egregore-branch-consent");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generatedBy": "bin/pi-render-spec.mjs",
|
|
3
3
|
"source": "AGENTS.md generated Codex spec",
|
|
4
|
-
"sourceHash": "
|
|
5
|
-
"outputHash": "
|
|
4
|
+
"sourceHash": "72d7c3986402",
|
|
5
|
+
"outputHash": "ac3aab4c039b",
|
|
6
6
|
"adaptations": [
|
|
7
7
|
"Pi project trust and APPEND_SYSTEM delivery",
|
|
8
8
|
"Pi tool_call branch gate",
|
package/runtime/pi/AGENTS.md
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
> `.pi/APPEND_SYSTEM.md`, and your workflows are exposed through the
|
|
15
15
|
> project-local `.pi/` runtime. Do not follow the Codex-specific block below.
|
|
16
16
|
>
|
|
17
|
+
> **If you are Prime Agent** (the `prime-agent` harness): **stop here.** Your
|
|
18
|
+
> authoritative Egregore instructions are loaded from
|
|
19
|
+
> `.prime/agent/APPEND_SYSTEM.md`, and your workflows are exposed through the
|
|
20
|
+
> project-local `.prime/agent/` runtime. Do not follow the Codex-specific
|
|
21
|
+
> block below.
|
|
22
|
+
>
|
|
17
23
|
> **If you are Codex or another shell-only agent:** this file is yours — continue.
|
|
18
24
|
|
|
19
25
|
Egregore is no longer only a Claude Code workspace. Claude Code remains the
|
|
@@ -395,7 +401,7 @@ Invoke commands from user intent — don't wait for the slash. Each command file
|
|
|
395
401
|
**Knowledge** — `$search` `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest` `$scroll` `$mock` `$audit`
|
|
396
402
|
**Identity** — `$me` (view profile or set display name)
|
|
397
403
|
**Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
|
|
398
|
-
**Connectors** — `$notion-connect` (Notion workspace) `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
|
|
404
|
+
**Connectors** — `$notion-connect` (Notion workspace) `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup) `$slack-connect` (Slack channel setup)
|
|
399
405
|
**Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
|
|
400
406
|
**Spirits** — `$summon` (persistent agent processes)
|
|
401
407
|
**Skills** — `$create-skill` (org-owned skill: scaffold, protect from updates, share)
|
|
@@ -411,7 +417,7 @@ Invoke commands from user intent — don't wait for the slash. Each command file
|
|
|
411
417
|
- Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
|
|
412
418
|
- Questions: `$ask [person]` (async) · just ask (agent answers from context)
|
|
413
419
|
- Ingestion: `$ingest <file-or-folder>` (org-scoped corpus intake) · `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
|
|
414
|
-
- Connectors: `$notion-connect` (Notion workspace) · `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in) · `$ingest notion` (promote Notion docs)
|
|
420
|
+
- Connectors: `$notion-connect` (Notion workspace) · `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$slack-connect` (Slack channel) · `$ingest` (bring content in) · `$ingest notion` (promote Notion docs)
|
|
415
421
|
- Identity: `$me` — "who am I", "call me oz"
|
|
416
422
|
- People: `$invite` (add) · `$delete-user` (remove)
|
|
417
423
|
- PRs: `$pr` (create) · `$review-pr` (review)
|
|
@@ -451,8 +457,9 @@ Egregore runs in one of two configurations, set by `mode` in `egregore.json`. De
|
|
|
451
457
|
|
|
452
458
|
**Hard rules in local mode:**
|
|
453
459
|
- Never tell the user to "ask their admin" for credentials. The user IS the admin.
|
|
454
|
-
- Never surface `api_url`, `EGREGORE_API_KEY`, or
|
|
455
|
-
-
|
|
460
|
+
- Never surface `api_url`, `EGREGORE_API_KEY`, or config edits as an upgrade path. Hosted Egregore is not something a user can turn on by adding fields to `egregore.json`.
|
|
461
|
+
- **The one sanctioned upgrade path is `egregore connect`** (the launcher flow: registers the org, provisions the key, replays the graph). Connect-tier connector skills carry an explicit upsell gate for local instances — deliver their message verbatim, offer the upgrade via `egregore connect` or a clean "not now", and stop on decline. Beyond that gate, do not improvise: no hand-set `api_url`, no partial flows.
|
|
462
|
+
- If a feature requires the hosted service and has no upsell gate, say so plainly ("this isn't available in this configuration") and stop.
|
|
456
463
|
- Calling `bin/graph.sh` or `bin/notify.sh` in local mode is harmless — they fail soft and return empty results — but there is no need to call them; prefer reading `memory/` directly.
|
|
457
464
|
|
|
458
465
|
**Connected mode** (`"mode": "connected"`, `api_url` set) — the hosted configuration, used by organizations on the hosted service. Full feature set: Neo4j knowledge graph via `bin/graph.sh`, Telegram notifications via `bin/notify.sh`, dashboard publication, API-backed context gathering on session start. Use `$env` to check API key, `$checkup` for diagnostics. If the graph is offline, show troubleshooting.
|
|
@@ -76,15 +76,11 @@ API_PID=$!
|
|
|
76
76
|
|
|
77
77
|
# Git/disk: runs in parallel (stays client-side)
|
|
78
78
|
(
|
|
79
|
-
#
|
|
79
|
+
# Refresh remote refs only. A data read must never mutate the working
|
|
80
|
+
# branch: the rebase this block used to run rewrote users' task branches as
|
|
81
|
+
# a side effect of /activity (observed rewinding a pushed branch mid-session).
|
|
82
|
+
# Workspace sync belongs to agent.sh sync / the session-start path.
|
|
80
83
|
git -C "$SCRIPT_DIR" fetch origin --quiet 2>/dev/null || true
|
|
81
|
-
CURRENT=$(git -C "$SCRIPT_DIR" branch --show-current 2>/dev/null || echo "unknown")
|
|
82
|
-
if [ "$CURRENT" != "develop" ]; then
|
|
83
|
-
git -C "$SCRIPT_DIR" fetch origin develop:develop --quiet 2>/dev/null || true
|
|
84
|
-
fi
|
|
85
|
-
if [[ "$CURRENT" == dev/* ]]; then
|
|
86
|
-
git -C "$SCRIPT_DIR" rebase develop --quiet >/dev/null 2>&1 || git -C "$SCRIPT_DIR" rebase --abort >/dev/null 2>&1 || true
|
|
87
|
-
fi
|
|
88
84
|
|
|
89
85
|
# Memory sync
|
|
90
86
|
if [ -d "$SCRIPT_DIR/memory/.git" ] || [ -L "$SCRIPT_DIR/memory" ]; then
|
|
@@ -99,6 +99,16 @@ _trigger_auto_thread() {
|
|
|
99
99
|
) >> "$LOGFILE" 2>&1 &
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
# Personal off switch for ambient capture. ripcord_enabled=false disables BOTH
|
|
103
|
+
# ripcord notes and auto-push of dirty worktrees — a user who opts out of
|
|
104
|
+
# ambient capture must not find silent commits either. The attendant's other
|
|
105
|
+
# duties (graph cache, context seed, handoff lifecycle) are unaffected.
|
|
106
|
+
# NOTE: jq's `//` treats false as empty (the auto_update:false gotcha), so the
|
|
107
|
+
# test compares the raw value, never `.key // true`.
|
|
108
|
+
_capture_enabled() {
|
|
109
|
+
[ "$(jq -r '.ripcord_enabled' "$MAIN_DIR/.egregore-state.json" 2>/dev/null)" != "false" ]
|
|
110
|
+
}
|
|
111
|
+
|
|
102
112
|
_author() {
|
|
103
113
|
local a
|
|
104
114
|
a=$(jq -r '.github_username // empty' "$MAIN_DIR/.egregore-state.json" 2>/dev/null)
|
|
@@ -249,6 +259,8 @@ _ripcord() {
|
|
|
249
259
|
local transcript="$1" base author date_ym date_d slug topic file prompts gitsum
|
|
250
260
|
base=$(basename "$transcript" .jsonl)
|
|
251
261
|
|
|
262
|
+
_capture_enabled || return 0
|
|
263
|
+
|
|
252
264
|
grep -q "$base" "$JOURNAL" 2>/dev/null && return 0
|
|
253
265
|
# Session already ended with ceremony? Then the attendant has no business
|
|
254
266
|
# here. Scan only the tail — a session that merely *read* the /handoff or
|
|
@@ -336,6 +348,7 @@ _ripcord() {
|
|
|
336
348
|
# --- Auto-push -------------------------------------------------------------
|
|
337
349
|
|
|
338
350
|
_auto_push() {
|
|
351
|
+
_capture_enabled || return 0
|
|
339
352
|
local wt branch dirty ahead upstream
|
|
340
353
|
git -C "$MAIN_DIR" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2}' | while read -r wt; do
|
|
341
354
|
branch=$(git -C "$wt" branch --show-current 2>/dev/null)
|
|
@@ -28,6 +28,17 @@ set -uo pipefail
|
|
|
28
28
|
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
29
29
|
STATE_FILE="$SCRIPT_DIR/.egregore-state.json"
|
|
30
30
|
|
|
31
|
+
# The attendant runs against the MAIN checkout and reads its state file, so
|
|
32
|
+
# ambient-capture settings must be written there — a worktree keeps its own
|
|
33
|
+
# copy, and writing the flag into it would silently do nothing.
|
|
34
|
+
MAIN_STATE_DIR="$SCRIPT_DIR"
|
|
35
|
+
if [ -f "$SCRIPT_DIR/.git" ]; then
|
|
36
|
+
_WT_GITDIR=$(sed 's/^gitdir: //' "$SCRIPT_DIR/.git" 2>/dev/null || true)
|
|
37
|
+
[ -n "$_WT_GITDIR" ] && MAIN_STATE_DIR=$(cd "$_WT_GITDIR/../../.." 2>/dev/null && pwd || echo "$SCRIPT_DIR")
|
|
38
|
+
fi
|
|
39
|
+
MAIN_STATE_FILE="$MAIN_STATE_DIR/.egregore-state.json"
|
|
40
|
+
[ -f "$MAIN_STATE_FILE" ] || MAIN_STATE_FILE="$STATE_FILE"
|
|
41
|
+
|
|
31
42
|
[ -f "$STATE_FILE" ] || echo '{}' > "$STATE_FILE"
|
|
32
43
|
|
|
33
44
|
# NB: jq's `//` swallows false — status would show "on" for enabled:false.
|
|
@@ -42,6 +53,11 @@ set_key() { # key value(json-encoded)
|
|
|
42
53
|
jq ".$1 = $2" "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
_set_main() { # key value(json-encoded) — writes where the attendant reads
|
|
57
|
+
jq ".$1 = $2" "$MAIN_STATE_FILE" > "$MAIN_STATE_FILE.tmp" \
|
|
58
|
+
&& mv "$MAIN_STATE_FILE.tmp" "$MAIN_STATE_FILE"
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
status() {
|
|
46
62
|
local enabled scope publish
|
|
47
63
|
enabled=$(get '.autosave_enabled' 'false')
|
|
@@ -50,6 +66,7 @@ status() {
|
|
|
50
66
|
echo "autosave: $([ "$enabled" = "true" ] && echo on || echo "off (opt-in — 'autosave on' to join the experiment)")"
|
|
51
67
|
echo "scope: $scope (all = handoffs + artifacts + docs · handoffs = memory repo only)"
|
|
52
68
|
echo "publish: $publish (gate = PRs wait for your merge · auto = merge to develop automatically)"
|
|
69
|
+
echo "ripcord: $(jq -r 'if (.ripcord_enabled != null) then (.ripcord_enabled|tostring) else "true" end' "$MAIN_STATE_FILE" 2>/dev/null || echo true) (crash-recovery notes + branch auto-push when a session dies without /wrap)"
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
case "${1:-status}" in
|
|
@@ -61,6 +78,15 @@ case "${1:-status}" in
|
|
|
61
78
|
all|handoffs) set_key autosave_scope "\"$2\"" && echo "scope: $2" ;;
|
|
62
79
|
*) echo "usage: autosave.sh scope all|handoffs" >&2; exit 2 ;;
|
|
63
80
|
esac ;;
|
|
81
|
+
ripcord)
|
|
82
|
+
# Ambient crash-recovery capture (the attendant): ripcord notes AND
|
|
83
|
+
# auto-push of dirty task branches. Separate from the autosave
|
|
84
|
+
# experiment above — this one defaults ON.
|
|
85
|
+
case "${2:-}" in
|
|
86
|
+
on) _set_main ripcord_enabled true && echo "ripcord: on — dead sessions leave a recovery note" ;;
|
|
87
|
+
off) _set_main ripcord_enabled false && echo "ripcord: off — no ambient notes, no branch auto-push" ;;
|
|
88
|
+
*) echo "usage: autosave.sh ripcord on|off" >&2; exit 2 ;;
|
|
89
|
+
esac ;;
|
|
64
90
|
publish)
|
|
65
91
|
case "${2:-}" in
|
|
66
92
|
gate|auto) set_key autosave_publish "\"$2\"" && echo "publish: $2" ;;
|
|
@@ -82,5 +108,5 @@ case "${1:-status}" in
|
|
|
82
108
|
echo "✗ PR #$n did not merge (conflict with develop?) — it stays open"
|
|
83
109
|
fi
|
|
84
110
|
done ;;
|
|
85
|
-
*) echo "usage: autosave.sh [status|on|off|scope all|handoffs|publish gate|auto|merge]" >&2; exit 2 ;;
|
|
111
|
+
*) echo "usage: autosave.sh [status|on|off|scope all|handoffs|publish gate|auto|merge|ripcord on|off]" >&2; exit 2 ;;
|
|
86
112
|
esac
|