@de-otio/repo-aegis-mcp 0.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/README.md +96 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +42 -0
- package/dist/server.js.map +1 -0
- package/dist/server.test.d.ts +2 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +171 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/_util.d.ts +26 -0
- package/dist/tools/_util.d.ts.map +1 -0
- package/dist/tools/_util.js +33 -0
- package/dist/tools/_util.js.map +1 -0
- package/dist/tools/audit.d.ts +20 -0
- package/dist/tools/audit.d.ts.map +1 -0
- package/dist/tools/audit.js +317 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/check.d.ts +3 -0
- package/dist/tools/check.d.ts.map +1 -0
- package/dist/tools/check.js +133 -0
- package/dist/tools/check.js.map +1 -0
- package/dist/tools/engagements.d.ts +3 -0
- package/dist/tools/engagements.d.ts.map +1 -0
- package/dist/tools/engagements.js +89 -0
- package/dist/tools/engagements.js.map +1 -0
- package/dist/tools/first-touch.d.ts +26 -0
- package/dist/tools/first-touch.d.ts.map +1 -0
- package/dist/tools/first-touch.js +49 -0
- package/dist/tools/first-touch.js.map +1 -0
- package/dist/tools/first-touch.test.d.ts +2 -0
- package/dist/tools/first-touch.test.d.ts.map +1 -0
- package/dist/tools/first-touch.test.js +224 -0
- package/dist/tools/first-touch.test.js.map +1 -0
- package/dist/tools/markers.d.ts +3 -0
- package/dist/tools/markers.d.ts.map +1 -0
- package/dist/tools/markers.js +121 -0
- package/dist/tools/markers.js.map +1 -0
- package/dist/tools/status.d.ts +11 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +68 -0
- package/dist/tools/status.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-touch.js","sourceRoot":"","sources":["../../src/tools/first-touch.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,qDAAqD;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,yDAAyD;AACzD,OAAO,EACL,kBAAkB,EAClB,SAAS,GACV,MAAM,0BAA0B,CAAC;AAOlC,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,CAAC;SACH,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,yGAAyG,CAC1G;CACJ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,4BAA4B,EAC5B;QACE,WAAW,EACT,iEAAiE;YACjE,mEAAmE;YACnE,kEAAkE;YAClE,mEAAmE;YACnE,4DAA4D;YAC5D,2BAA2B;QAC7B,WAAW;KACZ,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QAChB,MAAM,MAAM,GAAG,kBAAkB,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACzE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-touch.test.d.ts","sourceRoot":"","sources":["../../src/tools/first-touch.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
//
|
|
4
|
+
// Unit tests for the `aegis_classify_first_touch` MCP tool. The tests
|
|
5
|
+
// drive `firstTouchClassify` directly (the impl function exported from
|
|
6
|
+
// the tool module) so we can spin up real git fixtures and a real
|
|
7
|
+
// registry per case without paying the MCP transport overhead. The
|
|
8
|
+
// server-side smoke test in server.test.ts covers tool registration
|
|
9
|
+
// and one end-to-end call.
|
|
10
|
+
import { describe, it, before, after, beforeEach } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { firstTouchClassify, redactOrg } from "./first-touch.js";
|
|
17
|
+
let tmp;
|
|
18
|
+
let gitDir;
|
|
19
|
+
let nonGitDir;
|
|
20
|
+
let registryPath;
|
|
21
|
+
function git(cwd, args) {
|
|
22
|
+
execFileSync("git", args, {
|
|
23
|
+
cwd,
|
|
24
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function setRemote(cwd, url) {
|
|
28
|
+
try {
|
|
29
|
+
git(cwd, ["remote", "remove", "origin"]);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* not set */
|
|
33
|
+
}
|
|
34
|
+
git(cwd, ["remote", "add", "origin", url]);
|
|
35
|
+
}
|
|
36
|
+
function removeRemote(cwd) {
|
|
37
|
+
try {
|
|
38
|
+
git(cwd, ["remote", "remove", "origin"]);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
/* not set */
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function unsetClass(cwd) {
|
|
45
|
+
try {
|
|
46
|
+
execFileSync("git", ["config", "--unset", "repo-aegis.class"], {
|
|
47
|
+
cwd,
|
|
48
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
/* not set */
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
execFileSync("git", ["config", "--unset-all", "repo-aegis.engagement"], {
|
|
56
|
+
cwd,
|
|
57
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
/* not set */
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function writeRegistry(body) {
|
|
65
|
+
writeFileSync(registryPath, body, { mode: 0o600 });
|
|
66
|
+
}
|
|
67
|
+
before(() => {
|
|
68
|
+
tmp = mkdtempSync(join(tmpdir(), "repo-aegis-first-touch-"));
|
|
69
|
+
gitDir = join(tmp, "git");
|
|
70
|
+
nonGitDir = join(tmp, "non-git");
|
|
71
|
+
registryPath = join(tmp, "engagements.yaml");
|
|
72
|
+
mkdirSync(gitDir, { recursive: true });
|
|
73
|
+
mkdirSync(nonGitDir, { recursive: true });
|
|
74
|
+
git(gitDir, ["init", "-q", "-b", "main"]);
|
|
75
|
+
git(gitDir, ["config", "user.email", "test@example.com"]);
|
|
76
|
+
git(gitDir, ["config", "user.name", "test"]);
|
|
77
|
+
process.env["REPO_AEGIS_REGISTRY"] = registryPath;
|
|
78
|
+
});
|
|
79
|
+
after(() => {
|
|
80
|
+
delete process.env["REPO_AEGIS_REGISTRY"];
|
|
81
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
82
|
+
});
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
unsetClass(gitDir);
|
|
85
|
+
removeRemote(gitDir);
|
|
86
|
+
});
|
|
87
|
+
describe("firstTouchClassify — skipped paths", () => {
|
|
88
|
+
it("non-git cwd → status: skipped, reason: non-git", () => {
|
|
89
|
+
const result = firstTouchClassify({ cwd: nonGitDir });
|
|
90
|
+
assert.equal(result.status, "skipped");
|
|
91
|
+
if (result.status === "skipped") {
|
|
92
|
+
assert.equal(result.reason, "non-git");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
it("git repo with no remote → status: skipped, reason: no-remote", () => {
|
|
96
|
+
writeRegistry(`schemaVersion: 2\nengagements: []\n`);
|
|
97
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
98
|
+
assert.equal(result.status, "skipped");
|
|
99
|
+
if (result.status === "skipped") {
|
|
100
|
+
assert.equal(result.reason, "no-remote");
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
it("non-github remote → status: skipped, reason: non-github-host", () => {
|
|
104
|
+
writeRegistry(`schemaVersion: 2\nengagements: []\n`);
|
|
105
|
+
setRemote(gitDir, "git@gitlab.com:foo/bar.git");
|
|
106
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
107
|
+
assert.equal(result.status, "skipped");
|
|
108
|
+
if (result.status === "skipped") {
|
|
109
|
+
assert.equal(result.reason, "non-github-host");
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
it("registry not found → status: skipped, reason: registry-not-found", () => {
|
|
113
|
+
rmSync(registryPath, { force: true });
|
|
114
|
+
setRemote(gitDir, "git@github.com:foo/bar.git");
|
|
115
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
116
|
+
assert.equal(result.status, "skipped");
|
|
117
|
+
if (result.status === "skipped") {
|
|
118
|
+
assert.equal(result.reason, "registry-not-found");
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("firstTouchClassify — already-classified", () => {
|
|
123
|
+
it("returns status: already-classified when class is already set", () => {
|
|
124
|
+
writeRegistry(`schemaVersion: 2\nengagements: []\n`);
|
|
125
|
+
setRemote(gitDir, "git@github.com:foo/bar.git");
|
|
126
|
+
execFileSync("git", ["config", "repo-aegis.class", "private-strict"], {
|
|
127
|
+
cwd: gitDir,
|
|
128
|
+
});
|
|
129
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
130
|
+
assert.equal(result.status, "already-classified");
|
|
131
|
+
if (result.status === "already-classified") {
|
|
132
|
+
assert.equal(result.class, "private-strict");
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe("firstTouchClassify — applied", () => {
|
|
137
|
+
it("personalOrgs match → applied, class: public-eligible", () => {
|
|
138
|
+
writeRegistry(`schemaVersion: 2
|
|
139
|
+
personalOrgs: [my-handle]
|
|
140
|
+
engagements: []
|
|
141
|
+
`);
|
|
142
|
+
setRemote(gitDir, "git@github.com:my-handle/dotfiles.git");
|
|
143
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
144
|
+
assert.equal(result.status, "applied");
|
|
145
|
+
if (result.status === "applied") {
|
|
146
|
+
assert.equal(result.class, "public-eligible");
|
|
147
|
+
assert.equal(result.engagement, null);
|
|
148
|
+
assert.equal(result.markerWarning, null);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
it("engagement match with markers → applied, class: customer-coupled, markerWarning: null", () => {
|
|
152
|
+
writeRegistry(`schemaVersion: 2
|
|
153
|
+
engagements:
|
|
154
|
+
- id: foo-corp
|
|
155
|
+
name: Foo Corp
|
|
156
|
+
githubOrgs: [foo-corp]
|
|
157
|
+
markers: [foo-marker]
|
|
158
|
+
`);
|
|
159
|
+
setRemote(gitDir, "git@github.com:foo-corp/proj.git");
|
|
160
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
161
|
+
assert.equal(result.status, "applied");
|
|
162
|
+
if (result.status === "applied") {
|
|
163
|
+
assert.equal(result.class, "customer-coupled");
|
|
164
|
+
assert.equal(result.engagement, "foo-corp");
|
|
165
|
+
assert.equal(result.markerWarning, null);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
it("[SEC H-5] engagement match with zero markers → applied with markerWarning", () => {
|
|
169
|
+
writeRegistry(`schemaVersion: 2
|
|
170
|
+
engagements:
|
|
171
|
+
- id: bare-co
|
|
172
|
+
name: Bare Co
|
|
173
|
+
githubOrgs: [bare-co]
|
|
174
|
+
markers: []
|
|
175
|
+
`);
|
|
176
|
+
setRemote(gitDir, "git@github.com:bare-co/proj.git");
|
|
177
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
178
|
+
assert.equal(result.status, "applied");
|
|
179
|
+
if (result.status === "applied") {
|
|
180
|
+
assert.deepEqual(result.markerWarning, {
|
|
181
|
+
engagementId: "bare-co",
|
|
182
|
+
count: 0,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
describe("firstTouchClassify — needs-confirmation", () => {
|
|
188
|
+
it("unknown org → status: needs-confirmation with redactedOrg", () => {
|
|
189
|
+
writeRegistry(`schemaVersion: 2\nengagements: []\n`);
|
|
190
|
+
setRemote(gitDir, "git@github.com:unknown-org/proj.git");
|
|
191
|
+
const result = firstTouchClassify({ cwd: gitDir });
|
|
192
|
+
assert.equal(result.status, "needs-confirmation");
|
|
193
|
+
if (result.status === "needs-confirmation") {
|
|
194
|
+
assert.equal(result.org, "unknown-org");
|
|
195
|
+
assert.notEqual(result.redactedOrg, result.org);
|
|
196
|
+
assert.match(result.redactedOrg, /^un\*\*\*g$/);
|
|
197
|
+
assert.ok("newEngagement" in result.suggestion);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
it("does not mutate per-repo config on needs-confirmation", () => {
|
|
201
|
+
writeRegistry(`schemaVersion: 2\nengagements: []\n`);
|
|
202
|
+
setRemote(gitDir, "git@github.com:another-unknown/proj.git");
|
|
203
|
+
firstTouchClassify({ cwd: gitDir });
|
|
204
|
+
// git config --get exits 0 with value, or 1 if unset; we expect 1.
|
|
205
|
+
let threw = false;
|
|
206
|
+
try {
|
|
207
|
+
execFileSync("git", ["config", "--get", "repo-aegis.class"], {
|
|
208
|
+
cwd: gitDir,
|
|
209
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
threw = true;
|
|
214
|
+
}
|
|
215
|
+
assert.equal(threw, true, "class should not be set");
|
|
216
|
+
});
|
|
217
|
+
it("[SEC H-5] redactedOrg is the same as org when org < 4 chars", () => {
|
|
218
|
+
assert.equal(redactOrg("ab"), "ab");
|
|
219
|
+
assert.equal(redactOrg("abc"), "abc");
|
|
220
|
+
assert.equal(redactOrg("abcd"), "ab***d");
|
|
221
|
+
assert.equal(redactOrg("acme-corp"), "ac***p");
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=first-touch.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-touch.test.js","sourceRoot":"","sources":["../../src/tools/first-touch.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/tools/markers.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsCzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgG5D"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { ALWAYS_FILE_STEM, computeDenySet, markersDir as defaultMarkersDir, readRepoConfig, redactMatch, } from "@de-otio/repo-aegis-core";
|
|
7
|
+
import { errorResult, jsonResult } from "./_util.js";
|
|
8
|
+
function readMarkerFiles(dir) {
|
|
9
|
+
if (!existsSync(dir))
|
|
10
|
+
return [];
|
|
11
|
+
return readdirSync(dir)
|
|
12
|
+
.filter(f => f.endsWith(".txt"))
|
|
13
|
+
.sort()
|
|
14
|
+
.map(f => {
|
|
15
|
+
const path = join(dir, f);
|
|
16
|
+
const lines = readFileSync(path, "utf8").split("\n");
|
|
17
|
+
const patterns = [];
|
|
18
|
+
for (const raw of lines) {
|
|
19
|
+
const trimmed = raw.trim();
|
|
20
|
+
if (trimmed.length === 0 || trimmed.startsWith(";"))
|
|
21
|
+
continue;
|
|
22
|
+
patterns.push(trimmed);
|
|
23
|
+
}
|
|
24
|
+
return { stem: f.replace(/\.txt$/, ""), path, patterns };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const testInput = {
|
|
28
|
+
input: z
|
|
29
|
+
.string()
|
|
30
|
+
.min(1)
|
|
31
|
+
.describe("The string to probe against this repo's scoped deny set."),
|
|
32
|
+
cwd: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Working directory for repo class/engagement lookup (defaults to server cwd)."),
|
|
36
|
+
};
|
|
37
|
+
export function registerMarkersTools(server) {
|
|
38
|
+
server.registerTool("repo_aegis_markers_test", {
|
|
39
|
+
description: "Probe whether a string would trip this repo's scoped deny set. " +
|
|
40
|
+
"Equivalent to `repo-aegis markers test <string> --json`. Returns " +
|
|
41
|
+
"the matching pattern's engagement attribution and index, but " +
|
|
42
|
+
"ALWAYS redacts the literal pattern (no `--verbose` path here). " +
|
|
43
|
+
"Use this when the agent suspects a string might be a marker but " +
|
|
44
|
+
"wants to confirm before mentioning it to the user.",
|
|
45
|
+
inputSchema: testInput,
|
|
46
|
+
}, async ({ input, cwd }) => {
|
|
47
|
+
const repo = readRepoConfig(cwd ?? process.cwd());
|
|
48
|
+
const denySet = computeDenySet(repo);
|
|
49
|
+
const inputPreview = redactMatch(input);
|
|
50
|
+
if (denySet.combinedRegex === "") {
|
|
51
|
+
return jsonResult({
|
|
52
|
+
action: "markers-test",
|
|
53
|
+
input: inputPreview,
|
|
54
|
+
hits: [],
|
|
55
|
+
repo: { class: repo.class, engagements: repo.engagements },
|
|
56
|
+
warnings: denySet.warnings,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const hits = [];
|
|
60
|
+
for (const f of denySet.files) {
|
|
61
|
+
const lines = readFileSync(f.path, "utf8").split("\n");
|
|
62
|
+
const patterns = [];
|
|
63
|
+
for (const raw of lines) {
|
|
64
|
+
const trimmed = raw.trim();
|
|
65
|
+
if (trimmed.length === 0 || trimmed.startsWith(";"))
|
|
66
|
+
continue;
|
|
67
|
+
patterns.push(trimmed);
|
|
68
|
+
}
|
|
69
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
70
|
+
const p = patterns[i];
|
|
71
|
+
let re;
|
|
72
|
+
try {
|
|
73
|
+
re = new RegExp(p, "i");
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (re.test(input)) {
|
|
79
|
+
hits.push({ fileStem: f.stem, index: i, preview: redactMatch(p) });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return jsonResult({
|
|
84
|
+
action: "markers-test",
|
|
85
|
+
input: inputPreview,
|
|
86
|
+
hits,
|
|
87
|
+
repo: { class: repo.class, engagements: repo.engagements },
|
|
88
|
+
warnings: denySet.warnings,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
// The orchestrator may also want a redacted listing of every active
|
|
92
|
+
// marker file. We expose the same shape as `repo-aegis markers list
|
|
93
|
+
// --json` (no --verbose path) so an agent can reason about which
|
|
94
|
+
// engagements have markers loaded without ever seeing literal patterns.
|
|
95
|
+
server.registerTool("repo_aegis_markers_list", {
|
|
96
|
+
description: "List active marker files (engagement-id + pattern count + per-pattern " +
|
|
97
|
+
"previews). Equivalent to `repo-aegis markers list --json`. Patterns " +
|
|
98
|
+
"are ALWAYS redacted; the `--verbose` literal-reveal path is " +
|
|
99
|
+
"deliberately not exposed over MCP.",
|
|
100
|
+
inputSchema: {},
|
|
101
|
+
}, async () => {
|
|
102
|
+
const dir = defaultMarkersDir();
|
|
103
|
+
const files = readMarkerFiles(dir);
|
|
104
|
+
return jsonResult({
|
|
105
|
+
action: "markers-list",
|
|
106
|
+
markersDir: dir,
|
|
107
|
+
files: files.map(f => ({
|
|
108
|
+
stem: f.stem,
|
|
109
|
+
path: f.path,
|
|
110
|
+
isAlwaysBlock: f.stem === ALWAYS_FILE_STEM,
|
|
111
|
+
patternCount: f.patterns.length,
|
|
112
|
+
patterns: f.patterns.map((p, i) => ({ index: i, preview: redactMatch(p) })),
|
|
113
|
+
})),
|
|
114
|
+
verbose: false,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
// Silence unused-import warnings for `errorResult` (kept for future
|
|
118
|
+
// structured-error returns from this module).
|
|
119
|
+
void errorResult;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=markers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markers.js","sourceRoot":"","sources":["../../src/tools/markers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* `repo_aegis_status` — equivalent to `repo-aegis status --json`.
|
|
4
|
+
*
|
|
5
|
+
* Returns the repo's class, allowed engagements, deny-set summary, and
|
|
6
|
+
* any warnings. This is the first tool an agent should call when
|
|
7
|
+
* landing in a new repo: every other gate (`check`, `audit`) depends on
|
|
8
|
+
* this configuration.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerStatusTool(server: McpServer): void;
|
|
11
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsD1D"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { computeDenySet, isActive, loadRegistry, readRepoConfig, RegistryNotFoundError, } from "@de-otio/repo-aegis-core";
|
|
5
|
+
import { jsonResult } from "./_util.js";
|
|
6
|
+
const inputSchema = {
|
|
7
|
+
cwd: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Working directory to inspect (defaults to the server process's cwd). Use the repo root for accurate class detection."),
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* `repo_aegis_status` — equivalent to `repo-aegis status --json`.
|
|
14
|
+
*
|
|
15
|
+
* Returns the repo's class, allowed engagements, deny-set summary, and
|
|
16
|
+
* any warnings. This is the first tool an agent should call when
|
|
17
|
+
* landing in a new repo: every other gate (`check`, `audit`) depends on
|
|
18
|
+
* this configuration.
|
|
19
|
+
*/
|
|
20
|
+
export function registerStatusTool(server) {
|
|
21
|
+
server.registerTool("repo_aegis_status", {
|
|
22
|
+
description: "Show the repo's class, allowed engagements, and deny-set summary. " +
|
|
23
|
+
"Equivalent to `repo-aegis status --json`. Call this first when " +
|
|
24
|
+
"landing in a new repo — every other tool (check, audit) depends on " +
|
|
25
|
+
"this configuration.",
|
|
26
|
+
inputSchema,
|
|
27
|
+
}, async ({ cwd }) => {
|
|
28
|
+
const repo = readRepoConfig(cwd ?? process.cwd());
|
|
29
|
+
let registryEngagements = [];
|
|
30
|
+
let alwaysBlockCount = 0;
|
|
31
|
+
try {
|
|
32
|
+
const reg = loadRegistry();
|
|
33
|
+
registryEngagements = reg.engagements.map(e => ({
|
|
34
|
+
id: e.id,
|
|
35
|
+
name: e.name,
|
|
36
|
+
active: isActive(e),
|
|
37
|
+
}));
|
|
38
|
+
alwaysBlockCount = reg.alwaysBlock.length;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (!(err instanceof RegistryNotFoundError))
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
const denySet = computeDenySet(repo);
|
|
45
|
+
const allowed = repo.engagements.map(id => {
|
|
46
|
+
const meta = registryEngagements.find(e => e.id === id);
|
|
47
|
+
return { id, name: meta?.name ?? id, active: meta?.active ?? false };
|
|
48
|
+
});
|
|
49
|
+
const repoJson = {
|
|
50
|
+
cwd: repo.cwd,
|
|
51
|
+
isGitRepo: repo.isGitRepo,
|
|
52
|
+
class: repo.class,
|
|
53
|
+
classExplicit: repo.classExplicit,
|
|
54
|
+
engagements: repo.engagements,
|
|
55
|
+
};
|
|
56
|
+
return jsonResult({
|
|
57
|
+
repo: repoJson,
|
|
58
|
+
allowedEngagements: allowed,
|
|
59
|
+
denySet: {
|
|
60
|
+
files: denySet.files.map(f => f.stem),
|
|
61
|
+
patternCount: denySet.patterns.length,
|
|
62
|
+
},
|
|
63
|
+
alwaysBlock: { patternCount: alwaysBlockCount },
|
|
64
|
+
warnings: denySet.warnings,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,qDAAqD;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,qBAAqB,GAGtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,CAAC;SACH,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,sHAAsH,CACvH;CACJ,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,WAAW,EACT,oEAAoE;YACpE,iEAAiE;YACjE,qEAAqE;YACrE,qBAAqB;QACvB,WAAW;KACZ,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAElD,IAAI,mBAAmB,GAAoD,EAAE,CAAC;QAC9E,IAAI,gBAAgB,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;YAC3B,mBAAmB,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC9C,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;aACpB,CAAC,CAAC,CAAC;YACJ,gBAAgB,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,GAAG,YAAY,qBAAqB,CAAC;gBAAE,MAAM,GAAG,CAAC;QACzD,CAAC;QAED,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,OAAO,GAAqB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAC1D,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACxD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,EAAE,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAa;YACzB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;QAEF,OAAO,UAAU,CAAC;YAChB,IAAI,EAAE,QAAQ;YACd,kBAAkB,EAAE,OAAO;YAC3B,OAAO,EAAE;gBACP,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBACrC,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;aACtC;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE;YAC/C,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;IACL,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@de-otio/repo-aegis-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Model Context Protocol server wrapping repo-aegis as agent-readable tools",
|
|
5
|
+
"license": "GPL-3.0-or-later",
|
|
6
|
+
"author": "Richard Myers",
|
|
7
|
+
"homepage": "https://github.com/de-otio/repo-aegis",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/de-otio/repo-aegis.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"repo-aegis-mcp": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"main": "dist/server.js",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/server.d.ts",
|
|
21
|
+
"import": "./dist/server.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc && chmod +x dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@de-otio/repo-aegis-core": "^0.2.0",
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
33
|
+
"zod": "^4.4.2"
|
|
34
|
+
}
|
|
35
|
+
}
|