@event4u/agent-config 3.1.0 → 3.2.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.
Files changed (52) hide show
  1. package/.agent-src/commands/analytics/prune.md +78 -0
  2. package/.agent-src/commands/analytics/show.md +107 -0
  3. package/.agent-src/commands/analytics.md +64 -0
  4. package/.agent-src/commands/knowledge/forget.md +104 -0
  5. package/.agent-src/commands/knowledge/ingest.md +122 -0
  6. package/.agent-src/commands/knowledge/list.md +102 -0
  7. package/.agent-src/commands/knowledge.md +75 -0
  8. package/.agent-src/scripts/update_roadmap_progress.py +1 -1
  9. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  10. package/.claude-plugin/marketplace.json +8 -1
  11. package/CHANGELOG.md +38 -218
  12. package/README.md +12 -2
  13. package/dist/discovery/deprecation-report.md +1 -1
  14. package/dist/discovery/discovery-manifest.json +162 -8
  15. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  16. package/dist/discovery/discovery-manifest.summary.md +3 -3
  17. package/dist/discovery/orphan-report.md +1 -1
  18. package/dist/discovery/packs.json +12 -5
  19. package/dist/discovery/trust-report.md +2 -2
  20. package/dist/discovery/workspaces.json +11 -4
  21. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -0
  22. package/dist/mcp/registry-manifest.json +5 -3
  23. package/docs/architecture.md +1 -1
  24. package/docs/archive/CHANGELOG-pre-3.2.0.md +268 -0
  25. package/docs/catalog.md +9 -2
  26. package/docs/contracts/CHANGELOG-conventions.md +19 -0
  27. package/docs/contracts/at-rest-encryption.md +146 -0
  28. package/docs/contracts/daily-workspace.md +137 -0
  29. package/docs/contracts/explain-modes.md +146 -0
  30. package/docs/contracts/host-agent-protocol.md +88 -0
  31. package/docs/contracts/local-analytics.md +148 -0
  32. package/docs/contracts/local-knowledge-ingestion.md +96 -0
  33. package/docs/contracts/role-experience.md +121 -0
  34. package/docs/contracts/workspace-documents.md +140 -0
  35. package/docs/decisions/ADR-022-daily-workspace-decomposition.md +140 -0
  36. package/docs/decisions/ADR-023-host-agent-protocol.md +129 -0
  37. package/docs/decisions/ADR-024-workspace-v0-feature-floor.md +126 -0
  38. package/docs/decisions/ADR-025-workspace-chrome.md +119 -0
  39. package/docs/decisions/ADR-026-explain-mode-translation.md +117 -0
  40. package/docs/deploy/small-team-recipe.md +148 -0
  41. package/docs/deploy/team-deployment-posture.md +91 -0
  42. package/docs/getting-started-by-role.md +27 -0
  43. package/docs/getting-started.md +1 -1
  44. package/docs/guides/local-analytics.md +125 -0
  45. package/docs/guides/local-knowledge.md +127 -0
  46. package/package.json +4 -2
  47. package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  48. package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  49. package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  50. package/scripts/_lib/changelog_eras.py +330 -0
  51. package/scripts/memory_lookup.py +78 -1
  52. package/scripts/release.py +115 -5
@@ -12,7 +12,11 @@ Pipeline:
12
12
  since the last tag, render CHANGELOG section.
13
13
  3. Confirm — show preview, ask once (skippable with --yes).
14
14
  4. Branch + bump — create `release/X.Y.Z`, update package.json,
15
- .claude-plugin/marketplace.json, CHANGELOG.md.
15
+ .claude-plugin/marketplace.json, CHANGELOG.md,
16
+ then run `task release-prepare` so pack
17
+ manifests and tool projections pick up the
18
+ new version (otherwise the PR's own consistency
19
+ check fails — see PR #226 post-mortem).
16
20
  5. Commit + push — `release: X.Y.Z`, push branch, open PR.
17
21
  6. Wait for CI — `gh pr checks --watch` (skippable with --no-wait).
18
22
  7. Merge — `gh pr merge --merge --delete-branch`.
@@ -43,6 +47,16 @@ from dataclasses import dataclass
43
47
  from datetime import date as _date
44
48
  from pathlib import Path
45
49
 
50
+ from _lib.changelog_eras import (
51
+ CURRENT_ERA_BODY_CAP,
52
+ SplitPlan,
53
+ current_era_body_size,
54
+ current_era_insertion_point,
55
+ perform_split,
56
+ plan_split,
57
+ read_changelog_lines,
58
+ )
59
+
46
60
  REPO_ROOT = Path(__file__).resolve().parent.parent
47
61
  PACKAGE_JSON = REPO_ROOT / "package.json"
48
62
  MARKETPLACE_JSON = REPO_ROOT / ".claude-plugin" / "marketplace.json"
@@ -92,6 +106,10 @@ class Plan:
92
106
  last_tag: str | None
93
107
  changelog_body: str # rendered body (without the heading)
94
108
  changelog_entry: str # full entry including heading, for CHANGELOG.md
109
+ # Populated only when the release crosses an era boundary AND the
110
+ # current era body has grown past CURRENT_ERA_BODY_CAP. None for
111
+ # patch releases and for minor/major bumps where the era still fits.
112
+ split_plan: SplitPlan | None = None
95
113
 
96
114
 
97
115
  # ─── utilities ────────────────────────────────────────────────────────────────
@@ -449,16 +467,32 @@ def _render_test_trend_line(prev_tag: str | None) -> str | None:
449
467
 
450
468
 
451
469
  def prepend_changelog(path: Path, entry: str) -> None:
452
- """Insert `entry` directly above the most recent `## [` heading."""
470
+ """Insert ``entry`` inside the current era block.
471
+
472
+ Strategy delegates to ``current_era_insertion_point`` so a fresh
473
+ era (no version headings yet, just the intro blockquote) places the
474
+ new entry after the intro instead of appended at end-of-file. When
475
+ no current era header exists, falls back to the legacy "above the
476
+ most recent ## [" heuristic for safety.
477
+ """
453
478
  text = path.read_text(encoding="utf-8")
479
+ lines = text.splitlines()
480
+ insert_at = current_era_insertion_point(lines)
481
+ if insert_at is not None:
482
+ before = "\n".join(lines[:insert_at])
483
+ after = "\n".join(lines[insert_at:])
484
+ head = before + ("\n" if before else "")
485
+ path.write_text(head + entry + "\n" + after + "\n", encoding="utf-8")
486
+ return
487
+
488
+ # Legacy fallback — no era header present at all.
454
489
  marker_re = re.compile(r"^## \[?\d+\.\d+\.\d+", re.MULTILINE)
455
490
  m = marker_re.search(text)
456
491
  if not m:
457
- # No prior release heading — append after the introduction.
458
492
  path.write_text(text.rstrip() + "\n\n" + entry, encoding="utf-8")
459
493
  return
460
494
  before = text[: m.start()]
461
- after = text[m.start() :]
495
+ after = text[m.start():]
462
496
  path.write_text(before + entry + "\n" + after, encoding="utf-8")
463
497
 
464
498
 
@@ -573,6 +607,16 @@ def print_preview(plan: Plan) -> None:
573
607
  print(f" · {PACKAGE_JSON.relative_to(REPO_ROOT)}")
574
608
  print(f" · {MARKETPLACE_JSON.relative_to(REPO_ROOT)}")
575
609
  print(f" · {CHANGELOG.relative_to(REPO_ROOT)}")
610
+ print(" · regenerated derived files via `task release-prepare`")
611
+ print(" (packages/*/pack.yaml + README.md, .agent-src/, tool projections)")
612
+ if plan.split_plan is not None:
613
+ sp = plan.split_plan
614
+ print()
615
+ print("Era split (separate commit, before release commit):")
616
+ print(f" · archive → {sp.archive_path.relative_to(REPO_ROOT)}")
617
+ print(f" · old era → pre-{sp.boundary} (archived pointer)")
618
+ print(f" · new era → {sp.new_era_label} — current (empty body)")
619
+ print(f" · subject → {sp.commit_subject}")
576
620
  print()
577
621
  print("Changelog section:")
578
622
  print("─" * 72)
@@ -629,6 +673,39 @@ def execute(
629
673
  _step(1, total, f"Create branch {branch}")
630
674
  run("git", "checkout", "-b", branch)
631
675
 
676
+ # ─── 1b. era split (optional, separate commit) ─────────────────────────
677
+ # Lands as `chore(changelog): split era ...` BEFORE the release commit
678
+ # so the split is reviewable on its own and the release commit only
679
+ # touches the bump + new entry. Idempotent: archive already on disk
680
+ # OR a prior split commit on the branch is treated as already done.
681
+ if plan.split_plan is not None and not pr_merged:
682
+ sp = plan.split_plan
683
+ split_already_committed = (
684
+ sp.commit_subject
685
+ in git("log", f"{MAIN_BRANCH}..HEAD", "--format=%s", capture=True)
686
+ .splitlines()
687
+ )
688
+ if sp.archive_path.exists() and split_already_committed:
689
+ _step(
690
+ 1, total,
691
+ f"Era split for pre-{sp.boundary} already committed — skip",
692
+ )
693
+ elif sp.archive_path.exists() and not split_already_committed:
694
+ die(
695
+ f"era archive {sp.archive_path.relative_to(REPO_ROOT)} exists "
696
+ "but no matching split commit found on this branch — inspect "
697
+ "manually before resuming"
698
+ )
699
+ else:
700
+ _step(
701
+ 1, total,
702
+ f"Split era {sp.old_era_label} → pre-{sp.boundary} "
703
+ f"(new era {sp.new_era_label})",
704
+ )
705
+ perform_split(sp)
706
+ run("git", "add", "-A")
707
+ run("git", "commit", "-m", sp.commit_subject)
708
+
632
709
  # ─── 2. file mutations ──────────────────────────────────────────────────
633
710
  if pr_merged:
634
711
  _step(2, total, "PR already merged — skip file bumps")
@@ -642,6 +719,15 @@ def execute(
642
719
  set_marketplace_version(MARKETPLACE_JSON, plan.target)
643
720
  prepend_changelog(CHANGELOG, plan.changelog_entry)
644
721
 
722
+ # Regenerate derived files (pack manifests, .agent-src/, tool
723
+ # projections) so the PR's own consistency check passes. Without
724
+ # this the bump only lands in package.json + marketplace.json and
725
+ # the Sync + Generate Tools Consistency gate fails on the release
726
+ # PR itself — exactly the failure mode PR #226 hit. `task
727
+ # release-prepare` is idempotent, so resume runs are safe.
728
+ _step(2, total, "Regenerate derived files (`task release-prepare`)")
729
+ run("task", "release-prepare")
730
+
645
731
  # ─── 3. commit ──────────────────────────────────────────────────────────
646
732
  if pr_merged:
647
733
  _step(3, total, "PR already merged — skip commit")
@@ -652,7 +738,12 @@ def execute(
652
738
  _step(3, total, f"Last commit already `release: {plan.target}` and tree clean — skip")
653
739
  else:
654
740
  _step(3, total, f"Commit `release: {plan.target}`")
655
- run("git", "add", str(PACKAGE_JSON), str(MARKETPLACE_JSON), str(CHANGELOG))
741
+ # `git add -A` stages the three primary bump files AND every
742
+ # regenerated derived file (packages/*/pack.yaml + README.md,
743
+ # .agent-src/, .augment/, tool projections). Listing them
744
+ # explicitly would silently drift the moment a new generated
745
+ # tree is added.
746
+ run("git", "add", "-A")
656
747
  run("git", "commit", "-m", f"release: {plan.target}")
657
748
 
658
749
  # ─── 4. push ────────────────────────────────────────────────────────────
@@ -863,6 +954,24 @@ def main(argv: list[str] | None = None) -> int:
863
954
  full, body = render_changelog_entry(
864
955
  target, prev, commits, today, test_trend_line=test_trend_line
865
956
  )
957
+
958
+ # Era-split planning: only crosses the gate when the current era body
959
+ # has grown past the drift cap AND the release crosses a minor/major
960
+ # boundary. Patch overflow is caught by the drift test (red CI), not
961
+ # by an auto-split into a nonsensical "pre-X.Y.Z" archive.
962
+ split: SplitPlan | None = None
963
+ body_size = current_era_body_size()
964
+ if body_size > CURRENT_ERA_BODY_CAP:
965
+ candidate = plan_split(target)
966
+ if candidate is None:
967
+ die(
968
+ f"current era body is {body_size} lines (cap "
969
+ f"{CURRENT_ERA_BODY_CAP}) but release {target} is a patch "
970
+ f"within the same era — split needs a minor/major bump. "
971
+ "Cut a minor release or split CHANGELOG.md manually first."
972
+ )
973
+ split = candidate
974
+
866
975
  plan = Plan(
867
976
  current=current,
868
977
  target=target,
@@ -871,6 +980,7 @@ def main(argv: list[str] | None = None) -> int:
871
980
  last_tag=prev,
872
981
  changelog_body=body,
873
982
  changelog_entry=full,
983
+ split_plan=split,
874
984
  )
875
985
  print_preview(plan)
876
986
  if args.resume: