@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.
- package/.agent-src/contexts/execution/roadmap-process-loop.md +30 -4
- package/.agent-src/rules/linked-projects-onboarding-gate.md +82 -0
- package/.agent-src/rules/roadmap-progress-sync.md +39 -5
- package/.agent-src/scripts/update_roadmap_progress.py +63 -7
- package/.agent-src/skills/roadmap-management/SKILL.md +121 -21
- package/.agent-src/skills/roadmap-writing/SKILL.md +63 -0
- package/.agent-src/templates/agent-settings.md +16 -0
- package/.agent-src/templates/roadmaps.md +22 -1
- package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +20 -3
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +49 -0
- package/README.md +6 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +33 -11
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +5 -4
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/router.json +1 -1
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +3 -2
- package/docs/decisions/ADR-032-linked-projects-scope.md +118 -0
- package/docs/decisions/ADR-033-distribution-identity-npm-primary.md +81 -0
- package/docs/decisions/INDEX.md +2 -0
- package/docs/distribution/registries.md +29 -0
- package/docs/getting-started.md +1 -1
- package/docs/guides/cross-repo-linked-projects.md +86 -0
- package/package.json +1 -1
- package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/scripts/_lib/agent_settings.py +20 -3
- package/scripts/_lib/linked_projects.py +238 -0
- package/scripts/check_no_local_settings_committed.py +51 -0
- 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 `temporary` (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())
|