@event4u/agent-config 8.2.0 → 8.3.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 (44) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +38 -0
  3. package/MIGRATION.md +18 -0
  4. package/README.md +86 -3
  5. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  6. package/dist/discovery/deprecation-report.md +1 -1
  7. package/dist/discovery/discovery-manifest.json +1 -1
  8. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  9. package/dist/discovery/discovery-manifest.summary.md +1 -1
  10. package/dist/discovery/orphan-report.md +1 -1
  11. package/dist/discovery/packs.json +1 -1
  12. package/dist/discovery/trust-report.md +1 -1
  13. package/dist/discovery/workspaces.json +1 -1
  14. package/dist/install/install.mjs +586 -386
  15. package/dist/mcp/registry-manifest.json +2 -2
  16. package/docs/contracts/CHANGELOG-conventions.md +8 -5
  17. package/docs/contracts/branch-protection-policy.md +14 -5
  18. package/docs/contracts/mcp-phase-1-scope.md +75 -0
  19. package/docs/contracts/mcp-tool-inventory.md +22 -15
  20. package/docs/contracts/release-pr-gating.md +22 -11
  21. package/docs/decisions/ADR-111-canonical-glama-listing.md +97 -0
  22. package/docs/decisions/ADR-112-stdio-lite-stays-read-only.md +92 -0
  23. package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +201 -0
  24. package/docs/decisions/INDEX.md +3 -0
  25. package/docs/distribution/registry-submissions.md +1 -1
  26. package/docs/getting-started.md +8 -6
  27. package/docs/installation.md +13 -14
  28. package/docs/mcp-server.md +32 -20
  29. package/docs/setup/mcp-cloud-endpoints.md +9 -2
  30. package/package.json +2 -2
  31. package/src/scripts/_cli/cmd_doctor.ts +111 -11
  32. package/src/scripts/_cli/cmd_uninstall.ts +22 -0
  33. package/src/scripts/_cli/cmd_upgrade.ts +74 -13
  34. package/src/scripts/_lib/claude_settings_hooks.ts +251 -0
  35. package/src/scripts/condense.ts +7 -29
  36. package/src/scripts/install +23 -4
  37. package/src/scripts/install.ts +30 -0
  38. package/src/scripts/lint_glama_drift.ts +75 -0
  39. package/src/scripts/mcp_exec/safety_envelope.ts +105 -0
  40. package/src/scripts/mcp_registry_submit.sh +14 -3
  41. package/src/scripts/mcp_server/__main__.ts +6 -8
  42. package/src/scripts/mcp_server/consumer_tool_catalog.json +78 -3
  43. package/src/scripts/mcp_server/tools.ts +731 -0
  44. package/src/scripts/release.ts +203 -39
@@ -0,0 +1,105 @@
1
+ // Compiled safety envelope for MCP shell-exec tools.
2
+ //
3
+ // Lives OUTSIDE src/scripts/mcp_server/ on purpose: the A0 contract bans
4
+ // direct child_process imports in mcp_server modules; approved exec-tier
5
+ // tools lazy-import this module instead (same transitive pattern as the
6
+ // roadmap_archive handler's git effects — accepted by the 2026-07-07
7
+ // council verdict, agents/decisions/mcp-write-exec-cut-2026-07-07.md).
8
+ //
9
+ // The envelope is compiled policy, not configuration (council Decision 2):
10
+ // - argv array, never a shell string — caller-supplied values cannot be
11
+ // interpolated into a command line;
12
+ // - hard timeout — the child is killed, never awaited indefinitely;
13
+ // - output cap per stream — a runaway process cannot flood the MCP
14
+ // response;
15
+ // - long-running policy is synchronous truncation (roadmap Phase 5
16
+ // Step 4): no job queue, no polling — a call that exceeds the timeout
17
+ // returns timed_out: true with whatever output was captured.
18
+ //
19
+ // Network isolation is NOT promised here: a spawned test runner executes
20
+ // arbitrary project test code, which may itself use the network. The
21
+ // envelope constrains the spawn surface (no shell, bounded time, bounded
22
+ // output), not the semantics of the tool under test — stated explicitly
23
+ // in the A0 amendment.
24
+
25
+ import { execFile } from 'node:child_process';
26
+
27
+ export interface EnvelopeSpec {
28
+ /** argv[0] is the executable; the rest are literal arguments. No shell. */
29
+ readonly argv: readonly string[];
30
+ /** Working directory for the child (the consumer root). */
31
+ readonly cwd: string;
32
+ /** Hard kill deadline in milliseconds. */
33
+ readonly timeout_ms: number;
34
+ /** Per-stream output cap in bytes; excess is truncated, flagged. */
35
+ readonly max_output_bytes: number;
36
+ }
37
+
38
+ export interface EnvelopeResult {
39
+ exit_code: number | null;
40
+ timed_out: boolean;
41
+ stdout: string;
42
+ stdout_truncated: boolean;
43
+ stderr: string;
44
+ stderr_truncated: boolean;
45
+ duration_ms: number;
46
+ argv: string[];
47
+ }
48
+
49
+ function _truncate(text: string, cap: number): [string, boolean] {
50
+ const buf = Buffer.from(text, 'utf8');
51
+ if (buf.byteLength <= cap) {
52
+ return [text, false];
53
+ }
54
+ return [buf.subarray(0, cap).toString('utf8'), true];
55
+ }
56
+
57
+ /**
58
+ * Run `spec.argv` under the compiled envelope and resolve with a
59
+ * structured result. Never rejects for child failure — a non-zero exit,
60
+ * timeout, or spawn error all surface inside the result object so the
61
+ * MCP handler can return them as data.
62
+ */
63
+ export function run_enveloped(spec: EnvelopeSpec): Promise<EnvelopeResult> {
64
+ const started = Date.now();
65
+ const [cmd, ...args] = spec.argv;
66
+ if (cmd === undefined) {
67
+ throw new Error('envelope argv must not be empty');
68
+ }
69
+ return new Promise((resolve) => {
70
+ execFile(
71
+ cmd,
72
+ args,
73
+ {
74
+ cwd: spec.cwd,
75
+ timeout: spec.timeout_ms,
76
+ killSignal: 'SIGKILL',
77
+ windowsHide: true,
78
+ // Cap the in-memory buffer a little above the reported cap so
79
+ // truncation is ours (flagged), not execFile's (thrown).
80
+ maxBuffer: spec.max_output_bytes + 64 * 1024,
81
+ encoding: 'utf8',
82
+ },
83
+ (error, stdout, stderr) => {
84
+ const timedOut = Boolean(error && 'killed' in error && error.killed === true);
85
+ let exitCode: number | null = 0;
86
+ if (error) {
87
+ const code = (error as NodeJS.ErrnoException & { code?: unknown }).code;
88
+ exitCode = typeof code === 'number' ? code : null;
89
+ }
90
+ const [out, outTrunc] = _truncate(String(stdout ?? ''), spec.max_output_bytes);
91
+ const [err, errTrunc] = _truncate(String(stderr ?? ''), spec.max_output_bytes);
92
+ resolve({
93
+ exit_code: timedOut ? null : exitCode,
94
+ timed_out: timedOut,
95
+ stdout: out,
96
+ stdout_truncated: outTrunc,
97
+ stderr: err,
98
+ stderr_truncated: errTrunc,
99
+ duration_ms: Date.now() - started,
100
+ argv: [...spec.argv],
101
+ });
102
+ },
103
+ );
104
+ });
105
+ }
@@ -132,9 +132,16 @@ cd "$WORKDIR"
132
132
  gh repo clone "$FORK_REPO" registry-fork
133
133
  cd registry-fork
134
134
 
135
- # Add upstream + sync default branch.
135
+ # Add upstream + sync default branch. `gh repo clone` of a fork already
136
+ # wires an `upstream` remote pointing at the parent — make the add
137
+ # idempotent (set-url) instead of dying on "remote upstream already
138
+ # exists" (the exact failure observed on the 2026-07-07 live run).
136
139
  DEFAULT_BRANCH=$(gh repo view "$REGISTRY" --json defaultBranchRef --jq .defaultBranchRef.name)
137
- git remote add upstream "https://github.com/$REGISTRY.git"
140
+ if git remote get-url upstream >/dev/null 2>&1; then
141
+ git remote set-url upstream "https://github.com/$REGISTRY.git"
142
+ else
143
+ git remote add upstream "https://github.com/$REGISTRY.git"
144
+ fi
138
145
  git fetch upstream "$DEFAULT_BRANCH"
139
146
  git checkout -b "$BRANCH" "upstream/$DEFAULT_BRANCH"
140
147
 
@@ -155,10 +162,14 @@ _step "Pushing branch $BRANCH to fork"
155
162
  git push -u origin "$BRANCH"
156
163
 
157
164
  _step "Opening PR against $REGISTRY"
165
+ # gh pr create expects --head as OWNER:branch (not OWNER/REPO:branch) —
166
+ # the repo form fails with "Head sha can't be blank" (observed on the
167
+ # 2026-07-07 live run).
168
+ FORK_OWNER="${FORK_REPO%%/*}"
158
169
  PR_URL=$(gh pr create \
159
170
  --repo "$REGISTRY" \
160
171
  --base "$DEFAULT_BRANCH" \
161
- --head "$FORK_REPO:$BRANCH" \
172
+ --head "$FORK_OWNER:$BRANCH" \
162
173
  --title "Add event4u/agent-config — universal AI agent OS" \
163
174
  --body "$(cat <<'EOF'
164
175
  Adds [`event4u/agent-config`](https://github.com/event4u-app/agent-config) to the registry.
@@ -1,11 +1,9 @@
1
- // Entrypoint — TS twin of `scripts/mcp_server/__main__.py`
2
- // (`python -m scripts.mcp_server`).
3
- //
4
- // Required by Claude Desktop / Zed / Continue stdio-server config on the
5
- // Python side; the .py stays the runtime entry until a later phase (glama
6
- // still launches `python -m scripts.mcp_server`). This twin coexists and
7
- // forwards to `server.main()`; keep this file flat so crash stack traces
8
- // point at server.ts, not the bootstrap.
1
+ // Entrypoint for the MCP stdio server (`node node_modules/.bin/tsx
2
+ // src/scripts/mcp_server/__main__.ts`). glama launches this file directly
3
+ // via `internal/glama/run`; Claude Desktop / Zed / Continue stdio-server
4
+ // configs point at the same entry through `agent-config mcp:run`. Keep
5
+ // this file flat so crash stack traces point at server.ts, not the
6
+ // bootstrap.
9
7
  //
10
8
  // `node __main__.js` (or importing this module as the package main) runs
11
9
  // the server, mirroring the Python `if __name__ == "__main__": main()`
@@ -75,7 +75,7 @@
75
75
  "name": "memory_signal",
76
76
  "description": "Record an engineering-memory signal — a short, anchored observation such as a recurring bug pattern or an ownership note — to the monthly intake log `agents/memory/intake/signals-YYYY-MM.jsonl`. Use to capture a learning tied to a specific file so future `memory_lookup` calls surface it. Appends to the filesystem and is rate-limited per `(type, path)` within a rolling window. Returns the recorded signal.",
77
77
  "side_effect": "fs-write",
78
- "implemented_on": [],
78
+ "implemented_on": ["stdio"],
79
79
  "input_schema": {
80
80
  "type": "object",
81
81
  "properties": {
@@ -198,9 +198,9 @@
198
198
  },
199
199
  {
200
200
  "name": "run_tests",
201
- "description": "Run the consumer project's test suite via its auto-detected runner (Pest / PHPUnit, Jest / Vitest, pytest, …). Use to verify a change before claiming it works. Shell-bound. Returns the exit code, the runner name, and a structured tail of the output. Pass `filter` or `path` to narrow the run.",
201
+ "description": "Run the consumer project's vitest test suite under a compiled safety envelope: fixed argv (no shell interpolation), 120s timeout, 64KB output cap per stream. Shell-exec pilot per the 2026-07-07 council cut — vitest projects only; other runners (Pest / PHPUnit, pytest, Jest) return an error until a future council round approves them. Pass `filter` (vitest --testNamePattern) or `path` (in-tree file or directory) to narrow the run. Returns runner, passed, exit_code, timed_out, truncated stdout/stderr, and duration_ms.",
202
202
  "side_effect": "shell",
203
- "implemented_on": [],
203
+ "implemented_on": ["stdio"],
204
204
  "input_schema": {
205
205
  "type": "object",
206
206
  "properties": {
@@ -270,6 +270,81 @@
270
270
  "required": ["uri"],
271
271
  "additionalProperties": false
272
272
  }
273
+ },
274
+ {
275
+ "name": "roadmap_progress",
276
+ "description": "Regenerate `agents/roadmaps-progress.md` from the current checkbox state of every active roadmap. Use after landing roadmap work to keep the dashboard in sync without a shell round-trip. Writes the dashboard file inside the project tree. Set `dry_run: true` to compute counts without writing.",
277
+ "side_effect": "fs-write",
278
+ "implemented_on": ["stdio"],
279
+ "input_schema": {
280
+ "type": "object",
281
+ "properties": {
282
+ "dry_run": {"type": "boolean", "default": false, "description": "When true, return the computed dashboard without writing the file."}
283
+ },
284
+ "additionalProperties": false
285
+ }
286
+ },
287
+ {
288
+ "name": "roadmap_archive",
289
+ "description": "Archive every roadmap that has reached `count_open == 0` and was touched on the current branch — `git mv` to `agents/roadmaps/archive/`, migrate inbound references, and regenerate the dashboard. Use as the PR-gate sweep before opening a pull request. Mutates the git index (moves tracked files) but never commits or pushes.",
290
+ "side_effect": "fs-write",
291
+ "implemented_on": ["stdio"],
292
+ "input_schema": {"type": "object", "properties": {}, "additionalProperties": false}
293
+ },
294
+ {
295
+ "name": "capabilities_index",
296
+ "description": "Regenerate `CAPABILITIES.yaml`, the package's coverage index of skills, rules, commands, and guidelines. Use after adding or removing an artifact to keep the index current. Pass `check: true` to run in read-only CI mode (fails instead of writing on drift).",
297
+ "side_effect": "fs-write",
298
+ "implemented_on": ["stdio"],
299
+ "input_schema": {
300
+ "type": "object",
301
+ "properties": {
302
+ "check": {"type": "boolean", "default": false, "description": "When true, run in read-only drift-check mode instead of writing the index."}
303
+ },
304
+ "additionalProperties": false
305
+ }
306
+ },
307
+ {
308
+ "name": "doctor_report",
309
+ "description": "Run the consumer-project doctor diagnostic and return a structured health report (install drift, hook wiring, settings schema, discovery manifest freshness). Use to triage a misbehaving install. Read-only.",
310
+ "side_effect": "ro",
311
+ "implemented_on": ["stdio"],
312
+ "input_schema": {"type": "object", "properties": {}, "additionalProperties": false}
313
+ },
314
+ {
315
+ "name": "conformance_check",
316
+ "description": "Run the consumer conformance contract (`doctor --ci` plus installed-and-firing checks) and return pass/fail per check. Use to verify a consumer project's install is fully wired before relying on it. Read-only.",
317
+ "side_effect": "ro",
318
+ "implemented_on": ["stdio"],
319
+ "input_schema": {"type": "object", "properties": {}, "additionalProperties": false}
320
+ },
321
+ {
322
+ "name": "telemetry_report",
323
+ "description": "Return the artefact-engagement telemetry report — essential / useful / retirement-candidate skills and rules ranked by recorded consult+apply signals over a rolling window. Use to see which artifacts are actually load-bearing. Read-only. No-op (empty report) when telemetry recording is disabled.",
324
+ "side_effect": "ro",
325
+ "implemented_on": ["stdio"],
326
+ "input_schema": {
327
+ "type": "object",
328
+ "properties": {
329
+ "window_days": {"type": "integer", "minimum": 1, "default": 30, "description": "Reporting window in days. Defaults to 30."}
330
+ },
331
+ "additionalProperties": false
332
+ }
333
+ },
334
+ {
335
+ "name": "council_estimate",
336
+ "description": "Estimate the token cost of an AI-council debate over a given input (roadmap, diff, prompt, or file set) without spending — no network call, no billing. Use before deciding whether to authorize a real council run. Read-only.",
337
+ "side_effect": "ro",
338
+ "implemented_on": ["stdio"],
339
+ "input_schema": {
340
+ "type": "object",
341
+ "properties": {
342
+ "input_path": {"type": "string", "description": "Path to the roadmap, diff, or file to estimate council cost for."},
343
+ "depth": {"type": "string", "enum": ["shallow", "deep"], "description": "Council depth tier to estimate for. Defaults to the configured default depth."}
344
+ },
345
+ "required": ["input_path"],
346
+ "additionalProperties": false
347
+ }
273
348
  }
274
349
  ]
275
350
  }