@event4u/agent-config 8.2.0 → 8.4.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/.claude-plugin/marketplace.json +4 -426
- package/.claude-plugin/skills/install-agent-config/SKILL.md +32 -0
- package/CHANGELOG.md +57 -0
- package/MIGRATION.md +18 -0
- package/README.md +86 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/install/install.mjs +586 -386
- package/dist/mcp/registry-manifest.json +2 -2
- package/docs/contracts/CHANGELOG-conventions.md +8 -5
- package/docs/contracts/branch-protection-policy.md +14 -5
- package/docs/contracts/mcp-phase-1-scope.md +75 -0
- package/docs/contracts/mcp-tool-inventory.md +22 -15
- package/docs/contracts/release-pr-gating.md +22 -11
- package/docs/contracts/surface-matrix.md +103 -0
- package/docs/decisions/ADR-111-canonical-glama-listing.md +97 -0
- package/docs/decisions/ADR-112-stdio-lite-stays-read-only.md +92 -0
- package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +201 -0
- package/docs/decisions/INDEX.md +3 -0
- package/docs/distribution/registry-submissions.md +1 -1
- package/docs/getting-started.md +8 -6
- package/docs/installation.md +13 -14
- package/docs/mcp-server.md +32 -20
- package/docs/setup/mcp-cloud-endpoints.md +9 -2
- package/package.json +2 -2
- package/src/config/surface-matrix.yml +213 -0
- package/src/scripts/_cli/cmd_converge.ts +399 -0
- package/src/scripts/_cli/cmd_doctor.ts +203 -9
- package/src/scripts/_cli/cmd_uninstall.ts +22 -0
- package/src/scripts/_cli/cmd_upgrade.ts +101 -16
- package/src/scripts/_dispatch.bash +13 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +251 -0
- package/src/scripts/condense.ts +74 -65
- package/src/scripts/hook_manifest.yaml +16 -7
- package/src/scripts/install +23 -4
- package/src/scripts/install.ts +30 -0
- package/src/scripts/lint_glama_drift.ts +75 -0
- package/src/scripts/lint_marketplace.ts +54 -56
- package/src/scripts/lint_surface_matrix.ts +200 -0
- package/src/scripts/mcp_exec/safety_envelope.ts +105 -0
- package/src/scripts/mcp_registry_submit.sh +14 -3
- package/src/scripts/mcp_server/__main__.ts +6 -8
- package/src/scripts/mcp_server/consumer_tool_catalog.json +78 -3
- package/src/scripts/mcp_server/tools.ts +731 -0
- package/src/scripts/release.ts +203 -39
- package/src/scripts/surface_probe_hook.ts +231 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Lint src/config/surface-matrix.yml — the machine-checked per-tool
|
|
4
|
+
* canonical-surface inventory (road-to-install-path-convergence Phase 2).
|
|
5
|
+
*
|
|
6
|
+
* Guarantees:
|
|
7
|
+
* - every tool in install.ts::USER_SCOPE_PATHS has a matrix entry, and the
|
|
8
|
+
* matrix declares no tool the installer does not know (set equality)
|
|
9
|
+
* - each entry's scope_path matches USER_SCOPE_PATHS (drift fails)
|
|
10
|
+
* - surface / hooks values come from the closed enums
|
|
11
|
+
* - duplicate.detect.all_of paths are user-scope (~/) or repo-relative
|
|
12
|
+
* (./); repo-relative paths must resolve on disk
|
|
13
|
+
* - a defined duplicate class must carry a converge action, and the
|
|
14
|
+
* claude-code converge command must name the real plugin id (drift guard
|
|
15
|
+
* against _lib/claude_plugin.ts)
|
|
16
|
+
*
|
|
17
|
+
* Exit codes: 0 = clean, 1 = problems found, 3 = internal error.
|
|
18
|
+
*/
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
22
|
+
import YAML from 'yaml';
|
|
23
|
+
|
|
24
|
+
import { USER_SCOPE_PATHS } from './install.js';
|
|
25
|
+
import { CLAUDE_MARKETPLACE_NAME, CLAUDE_PLUGIN_ID } from './_lib/claude_plugin.js';
|
|
26
|
+
|
|
27
|
+
const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
28
|
+
export const MATRIX_PATH = path.join(REPO_ROOT, 'src', 'config', 'surface-matrix.yml');
|
|
29
|
+
|
|
30
|
+
const SURFACE_VALUES = new Set(['projection', 'plugin', 'bundles', 'export-only']);
|
|
31
|
+
const HOOKS_VALUES = new Set(['managed-settings-block', 'settings-hooks-opt-in', 'plugin', 'none']);
|
|
32
|
+
|
|
33
|
+
type JsonObject = Record<string, unknown>;
|
|
34
|
+
|
|
35
|
+
function isObject(v: unknown): v is JsonObject {
|
|
36
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SurfaceMatrixTool {
|
|
40
|
+
surface: string;
|
|
41
|
+
scope_path: string;
|
|
42
|
+
hooks: string;
|
|
43
|
+
duplicate?: {
|
|
44
|
+
description?: string;
|
|
45
|
+
pending_evidence?: string;
|
|
46
|
+
detect?: { all_of?: string[] };
|
|
47
|
+
};
|
|
48
|
+
converge?: { action?: string; command?: string; reaps?: string[] };
|
|
49
|
+
notes?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function load_surface_matrix(matrixPath: string = MATRIX_PATH): Record<string, SurfaceMatrixTool> {
|
|
53
|
+
const raw = YAML.parse(fs.readFileSync(matrixPath, 'utf-8'), { version: '1.1' }) as JsonObject;
|
|
54
|
+
const tools = raw['tools'];
|
|
55
|
+
if (!isObject(tools)) {
|
|
56
|
+
throw new Error('surface-matrix.yml: `tools` must be a mapping');
|
|
57
|
+
}
|
|
58
|
+
return tools as unknown as Record<string, SurfaceMatrixTool>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function main(): number {
|
|
62
|
+
if (!fs.existsSync(MATRIX_PATH)) {
|
|
63
|
+
process.stdout.write(`❌ ${MATRIX_PATH} not found\n`);
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const errors: string[] = [];
|
|
68
|
+
let tools: Record<string, SurfaceMatrixTool>;
|
|
69
|
+
try {
|
|
70
|
+
tools = load_surface_matrix();
|
|
71
|
+
} catch (e) {
|
|
72
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
73
|
+
process.stdout.write(`❌ surface-matrix.yml unreadable: ${msg}\n`);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Coverage — set equality with the installer's tool inventory.
|
|
78
|
+
const matrixIds = new Set(Object.keys(tools));
|
|
79
|
+
const installerIds = new Set(Object.keys(USER_SCOPE_PATHS));
|
|
80
|
+
for (const id of installerIds) {
|
|
81
|
+
if (!matrixIds.has(id)) {
|
|
82
|
+
errors.push(`tool \`${id}\` is in USER_SCOPE_PATHS but has no surface-matrix entry`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const id of matrixIds) {
|
|
86
|
+
if (!installerIds.has(id)) {
|
|
87
|
+
errors.push(`tool \`${id}\` is in the surface matrix but not in USER_SCOPE_PATHS`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const [id, entry] of Object.entries(tools)) {
|
|
92
|
+
const where = `tools.${id}`;
|
|
93
|
+
if (!isObject(entry)) {
|
|
94
|
+
errors.push(`${where} must be a mapping`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!SURFACE_VALUES.has(entry.surface)) {
|
|
98
|
+
errors.push(`${where}.surface \`${String(entry.surface)}\` not in {${[...SURFACE_VALUES].join(', ')}}`);
|
|
99
|
+
}
|
|
100
|
+
if (!HOOKS_VALUES.has(entry.hooks)) {
|
|
101
|
+
errors.push(`${where}.hooks \`${String(entry.hooks)}\` not in {${[...HOOKS_VALUES].join(', ')}}`);
|
|
102
|
+
}
|
|
103
|
+
const expectedPath = USER_SCOPE_PATHS[id];
|
|
104
|
+
if (expectedPath && entry.scope_path !== expectedPath) {
|
|
105
|
+
errors.push(
|
|
106
|
+
`${where}.scope_path \`${String(entry.scope_path)}\` drifted from ` +
|
|
107
|
+
`USER_SCOPE_PATHS \`${expectedPath}\``,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const dup = entry.duplicate;
|
|
112
|
+
if (dup !== undefined) {
|
|
113
|
+
if (!isObject(dup)) {
|
|
114
|
+
errors.push(`${where}.duplicate must be a mapping`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const detect = dup.detect;
|
|
118
|
+
if (detect !== undefined) {
|
|
119
|
+
const allOf = isObject(detect) ? detect['all_of'] : undefined;
|
|
120
|
+
if (!Array.isArray(allOf) || allOf.length === 0) {
|
|
121
|
+
errors.push(`${where}.duplicate.detect.all_of must be a non-empty list`);
|
|
122
|
+
} else {
|
|
123
|
+
for (const p of allOf) {
|
|
124
|
+
if (typeof p !== 'string' || !(p.startsWith('~/') || p.startsWith('./'))) {
|
|
125
|
+
errors.push(
|
|
126
|
+
`${where}.duplicate.detect path \`${String(p)}\` must start with ~/ or ./`,
|
|
127
|
+
);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (p.startsWith('./') && !fs.existsSync(path.join(REPO_ROOT, p.slice(2)))) {
|
|
131
|
+
errors.push(`${where}.duplicate.detect repo path does not resolve: \`${p}\``);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// A defined (non-pending) duplicate class needs a converge action.
|
|
136
|
+
const conv = entry.converge;
|
|
137
|
+
if (!isObject(conv) || typeof conv['command'] !== 'string' || conv['command'] === '') {
|
|
138
|
+
errors.push(`${where} defines a duplicate class but no converge.command`);
|
|
139
|
+
}
|
|
140
|
+
} else if (typeof dup['pending_evidence'] !== 'string') {
|
|
141
|
+
errors.push(
|
|
142
|
+
`${where}.duplicate must carry either detect.all_of or pending_evidence`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Drift guard: the claude-code converge command must name the real
|
|
149
|
+
// plugin id + marketplace name from _lib/claude_plugin.ts.
|
|
150
|
+
const claude = tools['claude-code'];
|
|
151
|
+
const claudeCmd = claude && isObject(claude.converge) ? String(claude.converge['command'] ?? '') : '';
|
|
152
|
+
const expectedRef = `${CLAUDE_PLUGIN_ID}@${CLAUDE_MARKETPLACE_NAME}`;
|
|
153
|
+
if (claudeCmd !== '' && !claudeCmd.includes(expectedRef)) {
|
|
154
|
+
errors.push(
|
|
155
|
+
`tools.claude-code.converge.command must reference \`${expectedRef}\` ` +
|
|
156
|
+
`(got \`${claudeCmd}\`)`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (errors.length > 0) {
|
|
161
|
+
process.stdout.write('❌ surface-matrix.yml has problems:\n');
|
|
162
|
+
for (const e of errors) {
|
|
163
|
+
process.stdout.write(` - ${e}\n`);
|
|
164
|
+
}
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
process.stdout.write(
|
|
169
|
+
`✅ surface-matrix.yml (${matrixIds.size} tools, coverage matches USER_SCOPE_PATHS)\n`,
|
|
170
|
+
);
|
|
171
|
+
process.stdout.write(' No issues found.\n');
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function _isCliEntry(): boolean {
|
|
176
|
+
if (process.argv[1] === undefined) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
180
|
+
if (import.meta.url === argvUrl) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
185
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
186
|
+
return here === argv;
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (_isCliEntry()) {
|
|
193
|
+
try {
|
|
194
|
+
process.exit(main());
|
|
195
|
+
} catch (exc) {
|
|
196
|
+
const msg = exc instanceof Error ? exc.message : String(exc);
|
|
197
|
+
process.stderr.write(`❌ internal error: ${msg}\n`);
|
|
198
|
+
process.exit(3);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -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
|
|
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 "$
|
|
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
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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
|
|
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
|
}
|