@event4u/agent-config 5.0.0 → 5.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 (38) hide show
  1. package/.agent-src/contexts/execution/roadmap-process-loop.md +30 -4
  2. package/.agent-src/rules/linked-projects-onboarding-gate.md +82 -0
  3. package/.agent-src/rules/roadmap-progress-sync.md +39 -5
  4. package/.agent-src/scripts/update_roadmap_progress.py +63 -7
  5. package/.agent-src/skills/roadmap-management/SKILL.md +121 -21
  6. package/.agent-src/skills/roadmap-writing/SKILL.md +63 -0
  7. package/.agent-src/templates/agent-settings.md +16 -0
  8. package/.agent-src/templates/roadmaps.md +22 -1
  9. package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +20 -3
  10. package/.claude-plugin/marketplace.json +1 -1
  11. package/CHANGELOG.md +49 -0
  12. package/README.md +6 -1
  13. package/dist/discovery/deprecation-report.md +1 -1
  14. package/dist/discovery/discovery-manifest.json +33 -11
  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 +6 -5
  19. package/dist/discovery/trust-report.md +3 -3
  20. package/dist/discovery/workspaces.json +5 -4
  21. package/dist/mcp/registry-manifest.json +2 -2
  22. package/dist/router.json +1 -1
  23. package/docs/architecture.md +1 -1
  24. package/docs/catalog.md +3 -2
  25. package/docs/decisions/ADR-032-linked-projects-scope.md +118 -0
  26. package/docs/decisions/ADR-033-distribution-identity-npm-primary.md +81 -0
  27. package/docs/decisions/INDEX.md +2 -0
  28. package/docs/distribution/registries.md +29 -0
  29. package/docs/getting-started.md +1 -1
  30. package/docs/guides/cross-repo-linked-projects.md +86 -0
  31. package/package.json +1 -1
  32. package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  33. package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  34. package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  35. package/scripts/_lib/agent_settings.py +20 -3
  36. package/scripts/_lib/linked_projects.py +238 -0
  37. package/scripts/check_no_local_settings_committed.py +51 -0
  38. package/scripts/lint_commit_subjects.py +139 -0
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python3
2
+ """Fail if any ``.agent-settings.local.yml`` is tracked by git.
3
+
4
+ `.agent-settings.local.yml` is the per-developer, per-machine override layer
5
+ (see ``scripts/_lib/agent_settings.py`` ``LOCAL_PROJECT_FILE``). It is
6
+ gitignored on purpose — committing one would leak one developer's local
7
+ machine paths (e.g. linked-project siblings) into everyone's checkout.
8
+
9
+ Exit 0 when none are tracked, 1 (with the offending paths) otherwise.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import subprocess
15
+ import sys
16
+
17
+ LOCAL_FILE = ".agent-settings.local.yml"
18
+
19
+
20
+ def tracked_local_settings() -> list[str]:
21
+ try:
22
+ out = subprocess.run(
23
+ ["git", "ls-files"],
24
+ check=True,
25
+ capture_output=True,
26
+ text=True,
27
+ ).stdout
28
+ except (subprocess.CalledProcessError, FileNotFoundError):
29
+ # Not a git repo / git missing — nothing to enforce here.
30
+ return []
31
+ return [
32
+ line
33
+ for line in out.splitlines()
34
+ if line.split("/")[-1] == LOCAL_FILE
35
+ ]
36
+
37
+
38
+ def main() -> int:
39
+ offenders = tracked_local_settings()
40
+ if not offenders:
41
+ print(f"✅ No tracked {LOCAL_FILE} files.")
42
+ return 0
43
+ print(f"❌ {LOCAL_FILE} must never be committed (per-machine local layer):")
44
+ for path in offenders:
45
+ print(f" 🔴 {path}")
46
+ print(f"\nRun: git rm --cached <path> — and confirm {LOCAL_FILE} is gitignored.")
47
+ return 1
48
+
49
+
50
+ if __name__ == "__main__":
51
+ sys.exit(main())
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env python3
2
+ """Reject commit subjects that would leak into the auto-generated changelog.
3
+
4
+ `scripts/release.py` reads commit subjects verbatim from
5
+ `<prev-tag>..HEAD` and writes them into `CHANGELOG.md` sections (notably
6
+ `### Breaking`). A sloppy subject — `wip`, `commit leftovers`, `fixup`,
7
+ short typos — becomes a sloppy public changelog line. Per
8
+ [ADR-033](../docs/decisions/ADR-033-distribution-identity-npm-primary.md)
9
+ and Phase 3 of `road-to-distribution-identity.md` this lint is the
10
+ CI-enforced gate that closes that surface.
11
+
12
+ Rules (PRO commit, range `<base>..<head>`):
13
+
14
+ - Subject length **after** stripping the Conventional-Commits type-prefix
15
+ (`feat:` / `fix(scope):` / `chore!:` / …) must be ≥ 10 characters.
16
+ - Subject must not contain blocklist words as whole tokens:
17
+ `leftover` / `leftovers` / `wip` / `temp` / `tmp` / `fixup`.
18
+ Case-insensitive; matched on word boundaries so legitimate uses like
19
+ `template` or `tempor­ary` (rare) survive — the linter targets the
20
+ short hand-wave forms reviewers see in feedback rounds.
21
+
22
+ Carve-outs:
23
+
24
+ - Merge commits (`Merge pull request …`, `Merge branch …`) are skipped —
25
+ they are GitHub-generated and not consumer-visible in the changelog.
26
+ - Revert commits (`Revert "…"`) are skipped — they ride the original
27
+ subject's discipline.
28
+
29
+ Local invocation defaults to `origin/main..HEAD` (the "what I am about
30
+ to push" range). CI sets `--base $GITHUB_BASE_REF --head $GITHUB_SHA`.
31
+
32
+ Cap: ≤ 150 LOC, stdlib only. Hooked into `task ci` via
33
+ `task lint-commit-subjects`.
34
+ """
35
+ from __future__ import annotations
36
+
37
+ import argparse
38
+ import re
39
+ import subprocess
40
+ import sys
41
+
42
+ BLOCKLIST = frozenset({"leftover", "leftovers", "wip", "temp", "tmp", "fixup"})
43
+ MIN_SUBJECT_LEN = 10
44
+
45
+ # Conventional Commits prefix — `type(scope)!?: message`. Matches the
46
+ # heads our `scripts/release.py` and CHANGELOG section logic respect.
47
+ CONVENTIONAL_PREFIX = re.compile(
48
+ r"^(feat|fix|chore|docs|refactor|test|perf|style|build|ci|revert)"
49
+ r"(\([^)]+\))?!?:\s+",
50
+ re.IGNORECASE,
51
+ )
52
+
53
+ # Skip lines — GitHub-generated merge subjects and revert commits.
54
+ SKIP_PREFIXES = ("Merge pull request", "Merge branch", "Merge remote-tracking",
55
+ 'Revert "')
56
+
57
+
58
+ def fetch_subjects(base: str, head: str) -> list[str]:
59
+ """Return one commit subject per element. Empty on no-range / no-commits."""
60
+ try:
61
+ result = subprocess.run(
62
+ ["git", "log", f"{base}..{head}", "--format=%s", "--no-merges"],
63
+ capture_output=True, text=True, check=True,
64
+ )
65
+ except subprocess.CalledProcessError as exc:
66
+ # CI without a proper base ref (force-push, first commit, weird state).
67
+ # Lint is advisory in that case — never block on git plumbing failures.
68
+ print(f"⚠️ git log {base}..{head} failed: {exc.stderr.strip()}",
69
+ file=sys.stderr)
70
+ return []
71
+ return [line.strip() for line in result.stdout.splitlines() if line.strip()]
72
+
73
+
74
+ def check_subject(subject: str) -> list[str]:
75
+ """Return list of issue strings for the subject; empty = clean."""
76
+ if any(subject.startswith(p) for p in SKIP_PREFIXES):
77
+ return []
78
+ issues: list[str] = []
79
+ body = CONVENTIONAL_PREFIX.sub("", subject, count=1)
80
+ if len(body) < MIN_SUBJECT_LEN:
81
+ issues.append(
82
+ f"subject body < {MIN_SUBJECT_LEN} chars after Conventional-Commits "
83
+ f"prefix: {subject!r}"
84
+ )
85
+ tokens = {t.lower() for t in re.findall(r"[A-Za-z]+", body)}
86
+ hits = sorted(tokens & BLOCKLIST)
87
+ if hits:
88
+ issues.append(
89
+ f"blocklist token(s) {hits} in subject: {subject!r}"
90
+ )
91
+ return issues
92
+
93
+
94
+ def main() -> int:
95
+ parser = argparse.ArgumentParser(description=__doc__)
96
+ parser.add_argument("--base", default="origin/main",
97
+ help="base ref (default: origin/main)")
98
+ parser.add_argument("--head", default="HEAD",
99
+ help="head ref (default: HEAD)")
100
+ parser.add_argument("--quiet", action="store_true",
101
+ help="suppress the clean-pass success line")
102
+ args = parser.parse_args()
103
+
104
+ subjects = fetch_subjects(args.base, args.head)
105
+ if not subjects:
106
+ if not args.quiet:
107
+ print(f"✅ No commit subjects to check in "
108
+ f"{args.base}..{args.head}.")
109
+ return 0
110
+
111
+ failures: list[tuple[str, str]] = []
112
+ for subj in subjects:
113
+ for issue in check_subject(subj):
114
+ failures.append((subj, issue))
115
+
116
+ if failures:
117
+ print(f"❌ {len(failures)} commit-subject issue(s) in "
118
+ f"{args.base}..{args.head}:", file=sys.stderr)
119
+ for _, issue in failures:
120
+ print(f" - {issue}", file=sys.stderr)
121
+ print(
122
+ "\nThese subjects feed the auto-generated CHANGELOG.md via "
123
+ "scripts/release.py — sloppy subjects become sloppy public "
124
+ "changelog lines. Per ADR-033 + "
125
+ "agents/roadmaps/road-to-distribution-identity.md § Phase 3.\n"
126
+ "Fix: rewrite the offending commits (e.g. `git rebase -i "
127
+ f"{args.base}` and `r`eword) with descriptive subjects, "
128
+ "then re-push.",
129
+ file=sys.stderr,
130
+ )
131
+ return 1
132
+
133
+ if not args.quiet:
134
+ print(f"✅ {len(subjects)} commit subject(s) clean.")
135
+ return 0
136
+
137
+
138
+ if __name__ == "__main__":
139
+ raise SystemExit(main())