@event4u/agent-config 2.12.0 → 2.14.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 (107) hide show
  1. package/.agent-src/commands/council/analysis.md +142 -0
  2. package/.agent-src/commands/council/debate.md +129 -0
  3. package/.agent-src/commands/council/default.md +8 -0
  4. package/.agent-src/commands/council/design.md +16 -12
  5. package/.agent-src/commands/council/optimize.md +16 -15
  6. package/.agent-src/commands/council/pr.md +12 -12
  7. package/.agent-src/commands/council.md +48 -2
  8. package/.agent-src/commands/memory/learn-low-impact.md +143 -0
  9. package/.agent-src/personas/advisors/contrarian.md +95 -0
  10. package/.agent-src/personas/advisors/executor.md +99 -0
  11. package/.agent-src/personas/advisors/expansionist.md +98 -0
  12. package/.agent-src/personas/advisors/first-principles.md +98 -0
  13. package/.agent-src/personas/advisors/outsider.md +102 -0
  14. package/.agent-src/rules/ask-when-uncertain.md +10 -6
  15. package/.agent-src/rules/copilot-routing.md +19 -0
  16. package/.agent-src/rules/devcontainer-routing.md +20 -0
  17. package/.agent-src/rules/external-reference-deep-dive.md +1 -1
  18. package/.agent-src/rules/fast-path-marker-visibility.md +38 -0
  19. package/.agent-src/rules/laravel-routing.md +20 -0
  20. package/.agent-src/rules/low-impact-corpus-privacy-floor.md +74 -0
  21. package/.agent-src/rules/symfony-routing.md +20 -0
  22. package/.agent-src/skills/ai-council/SKILL.md +388 -10
  23. package/.agent-src/skills/copilot-config/SKILL.md +1 -1
  24. package/.agent-src/skills/devcontainer/SKILL.md +1 -1
  25. package/.agent-src/skills/laravel/SKILL.md +1 -1
  26. package/.agent-src/skills/project-analysis-core/SKILL.md +1 -1
  27. package/.agent-src/skills/project-analyzer/SKILL.md +1 -1
  28. package/.agent-src/skills/symfony-workflow/SKILL.md +1 -1
  29. package/.agent-src/skills/universal-project-analysis/SKILL.md +1 -1
  30. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  31. package/.claude-plugin/marketplace.json +4 -1
  32. package/AGENTS.md +1 -1
  33. package/CHANGELOG.md +346 -124
  34. package/CONTRIBUTING.md +5 -0
  35. package/README.md +6 -6
  36. package/config/agent-settings.template.yml +5 -93
  37. package/config/gitignore-block.txt +6 -0
  38. package/docs/architecture/multi-tool-projection.md +53 -0
  39. package/docs/architecture/{compression.md → source-projection.md} +21 -3
  40. package/docs/architecture.md +15 -15
  41. package/docs/archive/CHANGELOG-pre-2.11.0.md +141 -0
  42. package/docs/catalog.md +25 -12
  43. package/docs/contracts/adr-architectural-consensus-mechanism.md +68 -0
  44. package/docs/contracts/adr-level-6-productization.md +7 -9
  45. package/docs/contracts/ai-council-config.md +658 -0
  46. package/docs/contracts/command-clusters.md +58 -2
  47. package/docs/contracts/command-surface-tiers.md +3 -2
  48. package/docs/contracts/cost-profile-defaults.md +5 -0
  49. package/docs/contracts/decision-engine-gates.md +5 -0
  50. package/docs/contracts/decision-trace-v1.md +2 -2
  51. package/docs/contracts/file-ownership-matrix.json +1735 -72
  52. package/docs/contracts/installed-tools-lockfile.md +2 -1
  53. package/docs/contracts/low-impact-corpus-format.md +95 -0
  54. package/docs/contracts/mcp-beta-criteria.md +6 -5
  55. package/docs/contracts/mcp-cloud-scope.md +5 -4
  56. package/docs/contracts/multi-tool-projection-fidelity.md +115 -0
  57. package/docs/contracts/release-trunk-sync.md +4 -3
  58. package/docs/contracts/tier-3-contrib-plugin.md +5 -6
  59. package/docs/getting-started.md +2 -2
  60. package/docs/guidelines/agent-infra/installed-tools-manifest.md +2 -1
  61. package/docs/installation.md +32 -0
  62. package/package.json +1 -1
  63. package/scripts/_archive/README.md +59 -0
  64. package/scripts/_cli/cmd_doctor.py +134 -0
  65. package/scripts/ai_council/_default_prices.py +10 -1
  66. package/scripts/ai_council/advisors.py +148 -0
  67. package/scripts/ai_council/airgap.py +165 -0
  68. package/scripts/ai_council/cli_hints.py +123 -0
  69. package/scripts/ai_council/clients.py +959 -5
  70. package/scripts/ai_council/compile_corpus.py +178 -0
  71. package/scripts/ai_council/confidence_gate.py +156 -0
  72. package/scripts/ai_council/config.py +1364 -0
  73. package/scripts/ai_council/consensus.py +329 -0
  74. package/scripts/ai_council/events_log.py +137 -0
  75. package/scripts/ai_council/learn_low_impact_preview.py +252 -0
  76. package/scripts/ai_council/low_impact.py +714 -0
  77. package/scripts/ai_council/low_impact_corpus.py +466 -0
  78. package/scripts/ai_council/low_impact_intake.py +163 -0
  79. package/scripts/ai_council/modes.py +6 -1
  80. package/scripts/ai_council/necessity.py +782 -0
  81. package/scripts/ai_council/orchestrator.py +872 -20
  82. package/scripts/ai_council/probation_gate.py +152 -0
  83. package/scripts/ai_council/prompts.py +335 -0
  84. package/scripts/ai_council/redact_low_impact_entry.py +155 -0
  85. package/scripts/ai_council/replay.py +155 -0
  86. package/scripts/ai_council/session.py +19 -1
  87. package/scripts/ai_council/shadow_dispatch.py +235 -0
  88. package/scripts/ai_council/solo_dispatch.py +226 -0
  89. package/scripts/audit_cloud_compatibility.py +74 -0
  90. package/scripts/audit_command_surface.py +363 -0
  91. package/scripts/check_compressed_paths.py +6 -1
  92. package/scripts/check_council_layout.py +11 -0
  93. package/scripts/ci_time_ratio.py +168 -0
  94. package/scripts/council_cli.py +2005 -30
  95. package/scripts/install.sh +12 -0
  96. package/scripts/measure_projection_bytes.py +159 -0
  97. package/scripts/measure_roadmap_trajectory.py +112 -0
  98. package/scripts/probe_projection_fidelity.py +202 -0
  99. package/scripts/score_skill_selection.py +198 -0
  100. package/scripts/skill_collision_clusters.py +162 -0
  101. /package/scripts/{_backfill_skill_domains.py → _archive/_backfill_skill_domains.py} +0 -0
  102. /package/scripts/{_bootstrap_tier_frontmatter.py → _archive/_bootstrap_tier_frontmatter.py} +0 -0
  103. /package/scripts/{_p43_bodies.py → _archive/_p43_bodies.py} +0 -0
  104. /package/scripts/{_p43_compress.py → _archive/_p43_compress.py} +0 -0
  105. /package/scripts/{_p4_migrate.py → _archive/_p4_migrate.py} +0 -0
  106. /package/scripts/{_phase2_shim_helper.py → _archive/_phase2_shim_helper.py} +0 -0
  107. /package/scripts/{_pilot_council_question.py → _archive/_pilot_council_question.py} +0 -0
@@ -0,0 +1,252 @@
1
+ """Preview builder for ``/memory learn-low-impact`` (step-9 Phase 7).
2
+
3
+ Default invocation is ``--preview``: build a structured plan describing
4
+ which Validated entries would be upstreamed to the package seed without
5
+ opening a PR. ``--apply`` (handled by the agent, not this module) is the
6
+ explicit opt-in that triggers the actual upstream-contribute PR flow.
7
+
8
+ The module is import-light by design — pure parsing + redaction + diff
9
+ rendering. PR creation lives in the ``upstream-contribute`` skill;
10
+ this module only hands the agent the material to surface.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+ from typing import Iterable
19
+
20
+ from scripts.ai_council.low_impact_corpus import (
21
+ CorpusEntry,
22
+ parse_corpus_strict,
23
+ )
24
+ from scripts.ai_council.redact_low_impact_entry import (
25
+ RedactionViolation,
26
+ redact_low_impact_entry,
27
+ )
28
+
29
+
30
+ _PROVENANCE_RE = re.compile(r"^last-upstreamed:\s*([0-9a-f]{6,40}|0+)\s*$",
31
+ re.IGNORECASE | re.MULTILINE)
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class PreviewEntry:
36
+ """One Validated bullet that would be upstreamed."""
37
+ phrase: str
38
+ normalised: str
39
+ line_no: int
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class RefusedEntry:
44
+ """A Validated bullet the redactor refused — never upstreams."""
45
+ phrase: str
46
+ line_no: int
47
+ violations: tuple[RedactionViolation, ...]
48
+
49
+ def reason(self) -> str:
50
+ return "; ".join(f"{v.category}: {v.snippet}" for v in self.violations)
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class LearnLowImpactPreview:
55
+ """Structured preview for ``/memory learn-low-impact --preview``.
56
+
57
+ Consumed by the agent which renders the human-facing preview block,
58
+ then waits for explicit ``--apply`` before invoking
59
+ :doc:`upstream-contribute </skills/upstream-contribute/SKILL>`.
60
+ """
61
+ promoted: tuple[PreviewEntry, ...]
62
+ refused: tuple[RefusedEntry, ...]
63
+ already_seeded: tuple[str, ...]
64
+ last_upstreamed_sha: str
65
+ seed_path: str
66
+ corpus_path: str
67
+ repo_slug: str = ""
68
+ warnings: tuple[str, ...] = field(default_factory=tuple)
69
+
70
+ @property
71
+ def has_work(self) -> bool:
72
+ return bool(self.promoted) or bool(self.refused)
73
+
74
+ @property
75
+ def would_open_pr(self) -> bool:
76
+ """True when ``--apply`` would actually open a PR.
77
+
78
+ Iron Law: any redactor refusal blocks the PR — the author must
79
+ rephrase or drop the offending entry locally and re-run.
80
+ """
81
+ return bool(self.promoted) and not self.refused
82
+
83
+ def render(self) -> str:
84
+ """Human-readable preview block.
85
+
86
+ Mirrors the rendering convention from ``/memory mine-session``:
87
+ a leading title line, then bucketed entries.
88
+ """
89
+ lines: list[str] = []
90
+ lines.append(
91
+ "## learn-low-impact preview"
92
+ + (f" — repo={self.repo_slug}" if self.repo_slug else "")
93
+ )
94
+ lines.append(f"last-upstreamed: {self.last_upstreamed_sha}")
95
+ lines.append(f"seed: {self.seed_path}")
96
+ lines.append("")
97
+ if self.promoted:
98
+ lines.append(f"### Promoted ({len(self.promoted)})")
99
+ for e in self.promoted:
100
+ lines.append(f"- \"{e.phrase}\" (line {e.line_no})")
101
+ lines.append("")
102
+ if self.refused:
103
+ lines.append(f"### Refused ({len(self.refused)}) — redactor blocked")
104
+ for r in self.refused:
105
+ lines.append(
106
+ f"- \"{r.phrase}\" (line {r.line_no}) — {r.reason()}"
107
+ )
108
+ lines.append("")
109
+ if self.already_seeded:
110
+ lines.append(f"### Already seeded ({len(self.already_seeded)})")
111
+ for phrase in self.already_seeded:
112
+ lines.append(f"- \"{phrase}\"")
113
+ lines.append("")
114
+ if not self.has_work:
115
+ lines.append("> No new validated entries to upstream.")
116
+ lines.append("")
117
+ if self.refused:
118
+ lines.append(
119
+ "> Refusals block the PR. Rephrase the entries locally"
120
+ " (or drop them) and re-run."
121
+ )
122
+ elif self.promoted:
123
+ lines.append(
124
+ "> Re-run with `--apply` to open the draft PR via"
125
+ " `upstream-contribute`."
126
+ )
127
+ return "\n".join(lines).rstrip() + "\n"
128
+
129
+ def render_diff(self) -> str:
130
+ """Source-project-stripped diff that ``--apply`` would propose.
131
+
132
+ Emits unified-diff-style ``+`` lines for each promoted phrase
133
+ under the seed file's ``## Validated`` section. The agent uses
134
+ this as the ``upstream-contribute`` patch body.
135
+ """
136
+ if not self.promoted:
137
+ return ""
138
+ lines = [f"--- {self.seed_path}", f"+++ {self.seed_path}"]
139
+ for e in self.promoted:
140
+ lines.append(f'+- "{e.phrase}"')
141
+ return "\n".join(lines) + "\n"
142
+
143
+ def render_pr_body(self) -> str:
144
+ """Draft PR body for the upstream contribute flow."""
145
+ n = len(self.promoted)
146
+ slug = self.repo_slug or "<repo-slug>"
147
+ title = f"feat(low-impact-seed): add {n} validated entries from {slug}"
148
+ body_lines: list[str] = [
149
+ f"# {title}",
150
+ "",
151
+ "Upstream from `/memory learn-low-impact --apply`.",
152
+ "",
153
+ "## Entries",
154
+ "",
155
+ ]
156
+ for e in self.promoted:
157
+ body_lines.append(f'- "{e.phrase}"')
158
+ body_lines.append("")
159
+ body_lines.append(
160
+ f"Provenance baseline: `{self.last_upstreamed_sha}`."
161
+ )
162
+ body_lines.append("")
163
+ body_lines.append(
164
+ "Per `low-impact-corpus-privacy-floor`, every entry above"
165
+ " cleared the redactor on intake and again at upstream."
166
+ )
167
+ return "\n".join(body_lines) + "\n"
168
+
169
+
170
+ def _read_seed_phrases(seed_path: Path) -> set[str]:
171
+ """Return the set of normalised phrases already in the seed file.
172
+
173
+ Missing seed file is not an error — it returns an empty set so the
174
+ first-ever upstream PR can seed the whole corpus. Reuses the
175
+ strict parser so the seed itself is contract-validated.
176
+ """
177
+ if not seed_path.exists():
178
+ return set()
179
+ result = parse_corpus_strict(seed_path)
180
+ return {e.normalised for e in result.validated}
181
+
182
+
183
+ def _read_provenance(corpus_path: Path) -> str:
184
+ if not corpus_path.exists():
185
+ return "0" * 40
186
+ text = corpus_path.read_text(encoding="utf-8")
187
+ m = _PROVENANCE_RE.search(text)
188
+ return m.group(1).lower() if m else "0" * 40
189
+
190
+
191
+ def build_preview(
192
+ corpus_path: "object",
193
+ seed_path: "object",
194
+ *,
195
+ repo_root: str | None = None,
196
+ private_domains: Iterable[str] = (),
197
+ customer_names: Iterable[str] = (),
198
+ sql_identifiers: Iterable[str] = (),
199
+ repo_slug: str = "",
200
+ ) -> LearnLowImpactPreview:
201
+ """Build the preview plan without performing any PR side-effects.
202
+
203
+ Steps mirror the command doc:
204
+
205
+ 1. Parse the local corpus (strict — drift surfaces as ParseError).
206
+ Step-10: the preview deliberately stays on the Markdown parser
207
+ (not the YAML lockfile) because it runs *before* ``task sync``
208
+ rebuilds the lockfile from a user's local corpus edits.
209
+ 2. Diff Validated entries against the upstream seed.
210
+ 3. Run the redactor on every candidate.
211
+ 4. Bucket into promoted / refused / already-seeded.
212
+ """
213
+ corpus_p = Path(str(corpus_path))
214
+ seed_p = Path(str(seed_path))
215
+ parsed = parse_corpus_strict(corpus_p)
216
+ seeded = _read_seed_phrases(seed_p)
217
+ promoted: list[PreviewEntry] = []
218
+ refused: list[RefusedEntry] = []
219
+ already: list[str] = []
220
+ for entry in parsed.validated:
221
+ if entry.normalised in seeded:
222
+ already.append(entry.phrase)
223
+ continue
224
+ result = redact_low_impact_entry(
225
+ entry.phrase,
226
+ repo_root=repo_root,
227
+ private_domains=private_domains,
228
+ customer_names=customer_names,
229
+ sql_identifiers=sql_identifiers,
230
+ )
231
+ if result.ok:
232
+ promoted.append(PreviewEntry(
233
+ phrase=entry.phrase,
234
+ normalised=entry.normalised,
235
+ line_no=entry.line_no,
236
+ ))
237
+ else:
238
+ refused.append(RefusedEntry(
239
+ phrase=entry.phrase,
240
+ line_no=entry.line_no,
241
+ violations=result.violations,
242
+ ))
243
+ return LearnLowImpactPreview(
244
+ promoted=tuple(promoted),
245
+ refused=tuple(refused),
246
+ already_seeded=tuple(already),
247
+ last_upstreamed_sha=_read_provenance(corpus_p),
248
+ seed_path=str(seed_p),
249
+ corpus_path=str(corpus_p),
250
+ repo_slug=repo_slug,
251
+ warnings=parsed.warnings,
252
+ )