@event4u/agent-config 5.4.1 → 5.6.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 (92) hide show
  1. package/.agent-src/commands/image/analyse.md +51 -0
  2. package/.agent-src/commands/image/create.md +53 -0
  3. package/.agent-src/commands/image/verify.md +48 -0
  4. package/.agent-src/commands/image.md +69 -0
  5. package/.agent-src/commands/knowledge/cross-repo.md +71 -0
  6. package/.agent-src/commands/knowledge.md +2 -0
  7. package/.agent-src/commands/skill/preview.md +67 -0
  8. package/.agent-src/commands/skill.md +48 -0
  9. package/.agent-src/commands/skills/discover.md +76 -0
  10. package/.agent-src/commands/skills.md +56 -0
  11. package/.agent-src/commands/video/from-song.md +351 -0
  12. package/.agent-src/commands/video.md +19 -9
  13. package/.agent-src/contexts/authority/commit-mechanics.md +8 -0
  14. package/.agent-src/rules/commit-policy.md +3 -8
  15. package/.agent-src/rules/linked-projects-onboarding-gate.md +1 -1
  16. package/.agent-src/rules/media-sync-ground-truth.md +58 -0
  17. package/.agent-src/skills/image-analyser/SKILL.md +121 -0
  18. package/.agent-src/skills/image-analyser/canon-spec.md +109 -0
  19. package/.agent-src/skills/image-analyser/evals/triggers.json +16 -0
  20. package/.agent-src/skills/image-creator/SKILL.md +117 -0
  21. package/.agent-src/skills/image-creator/evals/triggers.json +16 -0
  22. package/.agent-src/skills/song-to-script/SKILL.md +216 -0
  23. package/.claude-plugin/marketplace.json +15 -2
  24. package/CHANGELOG.md +84 -0
  25. package/CONTRIBUTING.md +6 -0
  26. package/README.md +3 -3
  27. package/config/agent-settings.template.yml +18 -0
  28. package/dist/cli/registry.js +1 -0
  29. package/dist/cli/registry.js.map +1 -1
  30. package/dist/discovery/deprecation-report.md +1 -1
  31. package/dist/discovery/discovery-manifest.json +327 -20
  32. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  33. package/dist/discovery/discovery-manifest.summary.md +4 -4
  34. package/dist/discovery/orphan-report.md +1 -1
  35. package/dist/discovery/packs.json +24 -10
  36. package/dist/discovery/trust-report.md +3 -3
  37. package/dist/discovery/workspaces.json +20 -6
  38. package/dist/mcp/registry-manifest.json +3 -3
  39. package/dist/router.json +1 -1
  40. package/dist/server/schemas/settings.js +4 -0
  41. package/dist/server/schemas/settings.js.map +1 -1
  42. package/docs/architecture.md +3 -3
  43. package/docs/catalog.md +20 -6
  44. package/docs/contracts/benchmark-report-schema.md +12 -10
  45. package/docs/contracts/command-clusters.md +5 -1
  46. package/docs/contracts/cross-repo-retrieval.md +64 -0
  47. package/docs/contracts/rule-router.md +39 -0
  48. package/docs/contracts/skill-discovery.md +80 -0
  49. package/docs/contracts/skill-dry-run.md +47 -0
  50. package/docs/contracts/value-dashboard-spec.md +7 -3
  51. package/docs/contracts/value-report-schema.md +6 -1
  52. package/docs/decisions/ADR-032-linked-projects-scope.md +7 -3
  53. package/docs/getting-started.md +2 -2
  54. package/docs/guides/cross-repo-linked-projects.md +7 -0
  55. package/docs/guides/cross-repo-retrieval.md +61 -0
  56. package/docs/guides/skill-discovery.md +71 -0
  57. package/docs/guides/skill-preview.md +71 -0
  58. package/docs/value.md +17 -17
  59. package/package.json +1 -1
  60. package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  61. package/scripts/_dispatch.bash +10 -0
  62. package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  63. package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  64. package/scripts/_lib/bench_report.py +13 -14
  65. package/scripts/_lib/bench_telegraph_report.py +1 -2
  66. package/scripts/_lib/token_count.py +95 -0
  67. package/scripts/_lib/value_report.py +3 -3
  68. package/scripts/ai-video/adapters/higgsfield.sh +163 -6
  69. package/scripts/ai-video/adapters/openai-images.sh +92 -6
  70. package/scripts/ai-video/lib/probe-audio.sh +181 -0
  71. package/scripts/audit_auto_rules.py +22 -6
  72. package/scripts/audit_command_surface.py +6 -1
  73. package/scripts/audit_initial_context.py +210 -0
  74. package/scripts/bench_ab_diff.py +4 -11
  75. package/scripts/bench_run.py +2 -3
  76. package/scripts/bench_runner.py +2 -2
  77. package/scripts/condense.py +44 -3
  78. package/scripts/cross_repo_retrieve.py +172 -0
  79. package/scripts/inventory_meta_layers.py +288 -0
  80. package/scripts/iron_law_sha.py +14 -5
  81. package/scripts/linked_projects_list.py +91 -0
  82. package/scripts/measure_rule_budget.py +15 -0
  83. package/scripts/memory_lookup.py +53 -2
  84. package/scripts/project_thin_rules.py +168 -0
  85. package/scripts/render_value_md.py +14 -23
  86. package/scripts/schemas/command.schema.json +1 -1
  87. package/scripts/schemas/rule.schema.json +1 -1
  88. package/scripts/schemas/skill.schema.json +2 -2
  89. package/scripts/skill_discovery.py +254 -0
  90. package/scripts/skill_linter.py +8 -4
  91. package/scripts/skill_preview.py +179 -0
  92. package/scripts/trigger_coverage.py +129 -0
@@ -0,0 +1,179 @@
1
+ """Skill preview — non-destructive "what will this skill do?" summary.
2
+
3
+ Phase 5 of `road-to-leaner-core-and-discovery`. Reads a skill's declared intent
4
+ (frontmatter + `## Steps` body) and renders a plain-language summary BEFORE the
5
+ skill runs. Read-only, no network, no execution.
6
+
7
+ NOT a sandbox: it surfaces declared intent, it does not run the skill or prove
8
+ side-effect-freeness (contract: docs/contracts/skill-dry-run.md). For
9
+ `execution: manual` skills (the default) it states "instructional only".
10
+
11
+ Usage:
12
+ python3 scripts/skill_preview.py <name> [--technical] [--format text|json]
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import json
18
+ import re
19
+ import sys
20
+ from pathlib import Path
21
+
22
+ import yaml
23
+
24
+ REPO_ROOT = Path(__file__).resolve().parent.parent
25
+ SKILLS_DIR = REPO_ROOT / ".agent-src" / "skills"
26
+
27
+ _CMD_RE = re.compile(r"`(python3?|bash|node|php|npm|task|pytest)\s+[^`]+`")
28
+ _PATH_RE = re.compile(r"`([\w./-]+\.(?:py|sh|md|json|yml|yaml|ts|js|php))`")
29
+
30
+
31
+ class PreviewError(Exception):
32
+ """Raised for a missing or malformed SKILL.md — rendered, never crashed on."""
33
+
34
+
35
+ def _split_frontmatter(text: str) -> tuple[dict, str]:
36
+ if not text.startswith("---"):
37
+ raise PreviewError("SKILL.md has no YAML frontmatter (missing leading `---`).")
38
+ end = text.find("\n---", 3)
39
+ if end == -1:
40
+ raise PreviewError("SKILL.md frontmatter is not closed (missing terminating `---`).")
41
+ try:
42
+ fm = yaml.safe_load(text[3:end]) or {}
43
+ except yaml.YAMLError as exc:
44
+ raise PreviewError(f"SKILL.md frontmatter is not valid YAML: {exc}")
45
+ if not isinstance(fm, dict):
46
+ raise PreviewError("SKILL.md frontmatter did not parse to a mapping.")
47
+ return fm, text[end + 4:]
48
+
49
+
50
+ def _steps(body: str) -> list[str]:
51
+ out: list[str] = []
52
+ in_steps = False
53
+ for line in body.splitlines():
54
+ if re.match(r"^##\s+Steps\b", line, re.IGNORECASE):
55
+ in_steps = True
56
+ continue
57
+ if in_steps and re.match(r"^##\s+\S", line): # next top-level section
58
+ break
59
+ if in_steps:
60
+ m = re.match(r"^###\s+(.*)", line)
61
+ if m:
62
+ out.append(m.group(1).strip())
63
+ return out
64
+
65
+
66
+ def _targets(body: str) -> tuple[list[str], list[str]]:
67
+ cmds = sorted({m.group(0).strip("`") for m in _CMD_RE.finditer(body)})
68
+ paths = sorted({m.group(1) for m in _PATH_RE.finditer(body)})
69
+ return cmds, paths
70
+
71
+
72
+ def load_preview(name: str) -> dict:
73
+ skill_dir = SKILLS_DIR / name
74
+ sk = skill_dir / "SKILL.md"
75
+ if not sk.is_file():
76
+ try:
77
+ shown = sk.relative_to(REPO_ROOT)
78
+ except ValueError:
79
+ shown = sk
80
+ raise PreviewError(f"no skill named {name!r} (looked for {shown}).")
81
+ fm, body = _split_frontmatter(sk.read_text(encoding="utf-8", errors="replace"))
82
+ execution = fm.get("execution") or {}
83
+ if not isinstance(execution, dict):
84
+ execution = {}
85
+ cmds, paths = _targets(body)
86
+ return {
87
+ "name": fm.get("name") or name,
88
+ "description": (fm.get("description") or "").strip(),
89
+ "domain": fm.get("domain") or "",
90
+ "execution_type": (execution.get("type") or "manual"),
91
+ "handler": (execution.get("handler") or "none"),
92
+ "allowed_tools": execution.get("allowed_tools") or [],
93
+ "command": execution.get("command") or [],
94
+ "steps": _steps(body),
95
+ "commands_named": cmds,
96
+ "paths_named": paths,
97
+ }
98
+
99
+
100
+ def render_plain(p: dict) -> str:
101
+ lines = [f"# Preview — `{p['name']}`", ""]
102
+ if p["description"]:
103
+ lines += [p["description"], ""]
104
+ etype = p["execution_type"]
105
+ if etype == "manual":
106
+ lines.append("**Execution: instructional only.** This skill does not run anything "
107
+ "automatically — it guides the agent step by step.")
108
+ elif etype == "assisted":
109
+ lines.append(f"**Execution: assisted** (handler `{p['handler']}`). It will *propose* actions "
110
+ "for you to approve — it never executes silently.")
111
+ else:
112
+ lines.append(f"**Execution: {etype}** (handler `{p['handler']}`). It can run actions; review the "
113
+ "declared tools and commands below before allowing it.")
114
+ lines.append("")
115
+ if p["steps"]:
116
+ lines.append("This skill will walk these steps:")
117
+ lines += [f"- {s}" for s in p["steps"]]
118
+ lines.append("")
119
+ if p["allowed_tools"]:
120
+ lines.append(f"Declared tools: {', '.join(p['allowed_tools'])}")
121
+ if p["command"]:
122
+ lines.append(f"Declared command: `{' '.join(str(c) for c in p['command'])}`")
123
+ if p["commands_named"]:
124
+ lines.append("Commands it may run:")
125
+ lines += [f"- `{c}`" for c in p["commands_named"]]
126
+ if p["paths_named"]:
127
+ lines.append("Files / scripts it references:")
128
+ lines += [f"- `{f}`" for f in p["paths_named"]]
129
+ if not (p["allowed_tools"] or p["command"] or p["commands_named"] or p["paths_named"]):
130
+ lines.append("_No tools, commands, or file targets declared — pure guidance._")
131
+ lines.append("")
132
+ lines.append("> Preview shows declared intent only — it does not run the skill or guarantee "
133
+ "side-effect-freeness. Contract: docs/contracts/skill-dry-run.md")
134
+ return "\n".join(lines)
135
+
136
+
137
+ def render_technical(p: dict) -> str:
138
+ lines = [f"# Preview (technical) — {p['name']}", "", "## Frontmatter (execution)", "```yaml"]
139
+ lines.append(f"execution_type: {p['execution_type']}")
140
+ lines.append(f"handler: {p['handler']}")
141
+ lines.append(f"allowed_tools: {p['allowed_tools']}")
142
+ if p["command"]:
143
+ lines.append(f"command: {p['command']}")
144
+ lines += ["```", "", "## Declared steps"]
145
+ lines += [f"{i+1}. {s}" for i, s in enumerate(p["steps"])] or ["(none)"]
146
+ if p["commands_named"]:
147
+ lines += ["", "## Commands named in body"] + [f"- `{c}`" for c in p["commands_named"]]
148
+ if p["paths_named"]:
149
+ lines += ["", "## Paths named in body"] + [f"- `{f}`" for f in p["paths_named"]]
150
+ return "\n".join(lines)
151
+
152
+
153
+ def main(argv: list[str] | None = None) -> int:
154
+ ap = argparse.ArgumentParser(description="Preview a skill's declared intent (read-only, no execution).")
155
+ ap.add_argument("name", help="Skill name (directory under .agent-src/skills/).")
156
+ ap.add_argument("--technical", action="store_true", help="Show raw frontmatter + step list.")
157
+ ap.add_argument("--format", choices=("text", "json"), default="text")
158
+ args = ap.parse_args(argv)
159
+
160
+ try:
161
+ preview = load_preview(args.name)
162
+ except PreviewError as exc:
163
+ if args.format == "json":
164
+ print(json.dumps({"error": str(exc), "name": args.name}, indent=2))
165
+ else:
166
+ print(f"❌ Cannot preview {args.name!r}: {exc}", file=sys.stderr)
167
+ return 2
168
+
169
+ if args.format == "json":
170
+ print(json.dumps(preview, indent=2))
171
+ elif args.technical:
172
+ print(render_technical(preview))
173
+ else:
174
+ print(render_plain(preview))
175
+ return 0
176
+
177
+
178
+ if __name__ == "__main__":
179
+ raise SystemExit(main())
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env python3
2
+ """Trigger-coverage suite (roadmap Phase 2.1 / 2.2).
3
+
4
+ The deterministic *must-load* floor for the lean-initial-context migration.
5
+ Before any auto-tier rule body is demoted to a router-resolved pointer
6
+ (Phase 3), this suite proves the router still fires that rule on
7
+ representative task phrasings — so a needed rule can never silently fail
8
+ to surface.
9
+
10
+ Cases live in `tests/eval/trigger-coverage.yaml` and have the shape:
11
+
12
+ - id: secrets-edit
13
+ prompt: "add a webhook secret to the billing service auth flow"
14
+ expect: [security-sensitive-stop] # MUST be in the fired set
15
+
16
+ Matching is deterministic against `dist/router.json` (NOT the semantic
17
+ production router — this is a reproducible floor that catches a removed
18
+ trigger in CI):
19
+
20
+ - kernel rules always fire (always-on layer).
21
+ - a tier rule fires iff any of its triggers matches the prompt:
22
+ - `keyword` → case-insensitive substring.
23
+ - `intent` → every alpha word (len>2) of the intent phrase appears as a
24
+ token in the prompt (so "structural decision" fires on a prompt that
25
+ contains both "structural" and "decision").
26
+
27
+ A case fails when an expected rule is NOT in the fired set. Exit 1 on any
28
+ miss → the merge that would have shrunk the rule is blocked (2.2).
29
+
30
+ Usage:
31
+ python3 scripts/trigger_coverage.py # run, human report
32
+ python3 scripts/trigger_coverage.py --json
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import argparse
38
+ import json
39
+ import re
40
+ import sys
41
+ from pathlib import Path
42
+
43
+ REPO_ROOT = Path(__file__).resolve().parent.parent
44
+ ROUTER = REPO_ROOT / "dist" / "router.json"
45
+ CORPUS = REPO_ROOT / "tests" / "eval" / "trigger-coverage.yaml"
46
+
47
+ try:
48
+ import yaml
49
+ except ImportError: # pragma: no cover
50
+ sys.stderr.write("error: PyYAML required (pip install pyyaml)\n")
51
+ sys.exit(2)
52
+
53
+ _WORD = re.compile(r"[a-z][a-z0-9_]+")
54
+
55
+
56
+ def _tokens(text: str) -> set[str]:
57
+ return {w for w in _WORD.findall(text.lower()) if len(w) > 2}
58
+
59
+
60
+ def load_router() -> dict:
61
+ return json.loads(ROUTER.read_text(encoding="utf-8"))
62
+
63
+
64
+ def fired_rules(prompt: str, router: dict) -> set[str]:
65
+ """Return every rule id the router would surface for `prompt`."""
66
+ low = prompt.lower()
67
+ toks = _tokens(prompt)
68
+ fired: set[str] = set(router.get("kernel", []))
69
+ for tier in ("tier_1", "tier_2"):
70
+ for entry in router.get(tier, []):
71
+ for trig in entry.get("triggers", []):
72
+ if "keyword" in trig:
73
+ if trig["keyword"].lower() in low:
74
+ fired.add(entry["id"])
75
+ break
76
+ elif "intent" in trig:
77
+ words = _tokens(trig["intent"])
78
+ if words and words <= toks:
79
+ fired.add(entry["id"])
80
+ break
81
+ return fired
82
+
83
+
84
+ def run(corpus: list[dict], router: dict) -> tuple[list[dict], int]:
85
+ results = []
86
+ misses = 0
87
+ for case in corpus:
88
+ fired = fired_rules(case["prompt"], router)
89
+ expected = case.get("expect", [])
90
+ missing = [r for r in expected if r not in fired]
91
+ ok = not missing
92
+ if not ok:
93
+ misses += 1
94
+ results.append({"id": case["id"], "ok": ok, "missing": missing,
95
+ "expect": expected})
96
+ return results, misses
97
+
98
+
99
+ def main(argv: list[str] | None = None) -> int:
100
+ ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
101
+ ap.add_argument("--json", action="store_true")
102
+ args = ap.parse_args(argv)
103
+
104
+ if not ROUTER.is_file():
105
+ sys.stderr.write(f"error: {ROUTER} missing — run compile_router first\n")
106
+ return 2
107
+ corpus = yaml.safe_load(CORPUS.read_text(encoding="utf-8")) or []
108
+ router = load_router()
109
+ results, misses = run(corpus, router)
110
+
111
+ if args.json:
112
+ print(json.dumps({"cases": len(results), "misses": misses,
113
+ "results": results}, indent=2, sort_keys=True))
114
+ else:
115
+ for r in results:
116
+ mark = "✅" if r["ok"] else "❌"
117
+ detail = "" if r["ok"] else f" MISSING: {', '.join(r['missing'])}"
118
+ print(f" {mark} {r['id']}{detail}")
119
+ print()
120
+ if misses:
121
+ print(f"❌ trigger-coverage: {misses}/{len(results)} case(s) failed — "
122
+ "a required rule does not fire. Blocking.")
123
+ else:
124
+ print(f"✅ trigger-coverage: {len(results)}/{len(results)} pass")
125
+ return 1 if misses else 0
126
+
127
+
128
+ if __name__ == "__main__":
129
+ sys.exit(main())